summaryrefslogtreecommitdiff
path: root/app/Http/Controllers/ListController.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Http/Controllers/ListController.php')
-rw-r--r--app/Http/Controllers/ListController.php3262
1 files changed, 1642 insertions, 1620 deletions
diff --git a/app/Http/Controllers/ListController.php b/app/Http/Controllers/ListController.php
index 39216721fe..03a2ffc687 100644
--- a/app/Http/Controllers/ListController.php
+++ b/app/Http/Controllers/ListController.php
@@ -36,1723 +36,1745 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Controller for lists of GEDCOM records.
*/
-class ListController extends AbstractBaseController {
- /**
- * Show a list of all individual or family records.
- *
- * @param Request $request
- *
- * @return Response
- */
- public function familyList(Request $request): Response {
- return $this->individualList($request);
- }
+class ListController extends AbstractBaseController
+{
+ /**
+ * Show a list of all individual or family records.
+ *
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function familyList(Request $request): Response
+ {
+ return $this->individualList($request);
+ }
- /**
- * Show a list of all individual or family records.
- *
- * @param Request $request
- *
- * @return Response
- */
- public function individualList(Request $request): Response {
- /** @var Tree $tree */
- $tree = $request->attributes->get('tree');
+ /**
+ * Show a list of all individual or family records.
+ *
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function individualList(Request $request): Response
+ {
+ /** @var Tree $tree */
+ $tree = $request->attributes->get('tree');
- /** @var User $user */
- $user = $request->attributes->get('user');
+ /** @var User $user */
+ $user = $request->attributes->get('user');
- // This action can show lists of both families and individuals.
- $route = $request->get('route');
- $families = $route === 'family-list';
+ // This action can show lists of both families and individuals.
+ $route = $request->get('route');
+ $families = $route === 'family-list';
- ob_start();
+ ob_start();
- // We show three different lists: initials, surnames and individuals
+ // We show three different lists: initials, surnames and individuals
- // All surnames beginning with this letter where "@"=unknown and ","=none
- $alpha = $request->get('alpha', '');
+ // All surnames beginning with this letter where "@"=unknown and ","=none
+ $alpha = $request->get('alpha', '');
- // All individuals with this surname
- $surname = $request->get('surname', '');
+ // All individuals with this surname
+ $surname = $request->get('surname', '');
- // All individuals
- $show_all = $request->get('show_all', 'no');
+ // All individuals
+ $show_all = $request->get('show_all', 'no');
- // Long lists can be broken down by given name
- $show_all_firstnames = $request->get('show_all_firstnames', 'no');
- if ($show_all_firstnames === 'yes') {
- $falpha = '';
- } else {
- $falpha = $request->get('falpha'); // All first names beginning with this letter
- }
+ // Long lists can be broken down by given name
+ $show_all_firstnames = $request->get('show_all_firstnames', 'no');
+ if ($show_all_firstnames === 'yes') {
+ $falpha = '';
+ } else {
+ $falpha = $request->get('falpha'); // All first names beginning with this letter
+ }
- $show_marnm = $request->get('show_marnm');
- switch ($show_marnm) {
- case 'no':
- case 'yes':
- $user->setPreference($route . '-marnm', $show_marnm);
- break;
- default:
- $show_marnm = $user->getPreference($route . '-marnm');
- }
+ $show_marnm = $request->get('show_marnm');
+ switch ($show_marnm) {
+ case 'no':
+ case 'yes':
+ $user->setPreference($route . '-marnm', $show_marnm);
+ break;
+ default:
+ $show_marnm = $user->getPreference($route . '-marnm');
+ }
- // Make sure selections are consistent.
- // i.e. can’t specify show_all and surname at the same time.
- if ($show_all === 'yes') {
- if ($show_all_firstnames === 'yes') {
- $alpha = '';
- $surname = '';
- $legend = I18N::translate('All');
- $params = [
- 'ged' => $tree->getName(),
- 'show_all' => 'yes',
- ];
- $show = 'indi';
- } elseif ($falpha) {
- $alpha = '';
- $surname = '';
- $legend = I18N::translate('All') . ', ' . e($falpha) . '…';
- $params = [
- 'ged' => $tree->getName(),
- 'show_all' => 'yes',
- ];
- $show = 'indi';
- } else {
- $alpha = '';
- $surname = '';
- $legend = I18N::translate('All');
- $params = [
- 'ged' => $tree->getName(),
- 'show_all' => 'yes',
- ];
- $show = $request->get('show', 'surn');
- }
- } elseif ($surname) {
- $alpha = $this->initialLetter($surname); // so we can highlight the initial letter
- $show_all = 'no';
- if ($surname === '@N.N.') {
- $legend = I18N::translateContext('Unknown surname', '…');
- } else {
- // The surname parameter is a root/canonical form.
- // Display it as the actual surname
- $legend = implode('/', array_keys($this->surnames($tree, $surname, $alpha, $show_marnm === 'yes', $families)));
- }
- $params = [
- 'ged' => $tree->getName(),
- 'surname' => $surname,
- 'falpha' => $falpha,
- ];
- switch ($falpha) {
- case '':
- break;
- case '@':
- $legend .= ', ' . I18N::translateContext('Unknown given name', '…');
- break;
- default:
- $legend .= ', ' . e($falpha) . '…';
- break;
- }
- $show = 'indi'; // SURN list makes no sense here
- } elseif ($alpha === '@') {
- $show_all = 'no';
- $legend = I18N::translateContext('Unknown surname', '…');
- $params = [
- 'alpha' => $alpha,
- 'ged' => $tree->getName(),
- ];
- $show = 'indi'; // SURN list makes no sense here
- } elseif ($alpha === ',') {
- $show_all = 'no';
- $legend = I18N::translate('None');
- $params = [
- 'alpha' => $alpha,
- 'ged' => $tree->getName(),
- ];
- $show = 'indi'; // SURN list makes no sense here
- } elseif ($alpha) {
- $show_all = 'no';
- $legend = e($alpha) . '…';
- $params = [
- 'alpha' => $alpha,
- 'ged' => $tree->getName(),
- ];
- $show = $request->get('show', 'surn');
- } else {
- $show_all = 'no';
- $legend = '…';
- $params = [
- 'ged' => $tree->getName(),
- ];
- $show = 'none'; // Don't show lists until something is chosen
- }
- $legend = '<span dir="auto">' . $legend . '</span>';
+ // Make sure selections are consistent.
+ // i.e. can’t specify show_all and surname at the same time.
+ if ($show_all === 'yes') {
+ if ($show_all_firstnames === 'yes') {
+ $alpha = '';
+ $surname = '';
+ $legend = I18N::translate('All');
+ $params = [
+ 'ged' => $tree->getName(),
+ 'show_all' => 'yes',
+ ];
+ $show = 'indi';
+ } elseif ($falpha) {
+ $alpha = '';
+ $surname = '';
+ $legend = I18N::translate('All') . ', ' . e($falpha) . '…';
+ $params = [
+ 'ged' => $tree->getName(),
+ 'show_all' => 'yes',
+ ];
+ $show = 'indi';
+ } else {
+ $alpha = '';
+ $surname = '';
+ $legend = I18N::translate('All');
+ $params = [
+ 'ged' => $tree->getName(),
+ 'show_all' => 'yes',
+ ];
+ $show = $request->get('show', 'surn');
+ }
+ } elseif ($surname) {
+ $alpha = $this->initialLetter($surname); // so we can highlight the initial letter
+ $show_all = 'no';
+ if ($surname === '@N.N.') {
+ $legend = I18N::translateContext('Unknown surname', '…');
+ } else {
+ // The surname parameter is a root/canonical form.
+ // Display it as the actual surname
+ $legend = implode('/', array_keys($this->surnames($tree, $surname, $alpha, $show_marnm === 'yes', $families)));
+ }
+ $params = [
+ 'ged' => $tree->getName(),
+ 'surname' => $surname,
+ 'falpha' => $falpha,
+ ];
+ switch ($falpha) {
+ case '':
+ break;
+ case '@':
+ $legend .= ', ' . I18N::translateContext('Unknown given name', '…');
+ break;
+ default:
+ $legend .= ', ' . e($falpha) . '…';
+ break;
+ }
+ $show = 'indi'; // SURN list makes no sense here
+ } elseif ($alpha === '@') {
+ $show_all = 'no';
+ $legend = I18N::translateContext('Unknown surname', '…');
+ $params = [
+ 'alpha' => $alpha,
+ 'ged' => $tree->getName(),
+ ];
+ $show = 'indi'; // SURN list makes no sense here
+ } elseif ($alpha === ',') {
+ $show_all = 'no';
+ $legend = I18N::translate('None');
+ $params = [
+ 'alpha' => $alpha,
+ 'ged' => $tree->getName(),
+ ];
+ $show = 'indi'; // SURN list makes no sense here
+ } elseif ($alpha) {
+ $show_all = 'no';
+ $legend = e($alpha) . '…';
+ $params = [
+ 'alpha' => $alpha,
+ 'ged' => $tree->getName(),
+ ];
+ $show = $request->get('show', 'surn');
+ } else {
+ $show_all = 'no';
+ $legend = '…';
+ $params = [
+ 'ged' => $tree->getName(),
+ ];
+ $show = 'none'; // Don't show lists until something is chosen
+ }
+ $legend = '<span dir="auto">' . $legend . '</span>';
- if ($families) {
- $title = I18N::translate('Families') . ' — ' . $legend;
- } else {
- $title = I18N::translate('Individuals') . ' — ' . $legend;
- }
+ if ($families) {
+ $title = I18N::translate('Families') . ' — ' . $legend;
+ } else {
+ $title = I18N::translate('Individuals') . ' — ' . $legend;
+ }
- ?>
- <div class="d-flex flex-column wt-page-options wt-page-options-individual-list d-print-none">
- <ul class="d-flex flex-wrap wt-initials-list">
+ ?>
+ <div class="d-flex flex-column wt-page-options wt-page-options-individual-list d-print-none">
+ <ul class="d-flex flex-wrap wt-initials-list">
- <?php
- foreach ($this->surnameAlpha($tree, $show_marnm === 'yes', $families) as $letter => $count) {
- echo '<li class="wt-initials-list-item">';
- if ($count > 0) {
- echo '<a href="' . e(route($route, [
- 'alpha' => $letter,
- 'ged' => $tree->getName(),
- ])) . '" class="wt-initial' . ($letter === $alpha ? ' active' : '') . '" title="' . I18N::number($count) . '">' . $this->surnameInitial((string) $letter) . '</a>';
- } else {
- echo '<span class="wt-initial text-muted">' . $this->surnameInitial((string) $letter) . '</span>';
- }
- echo '</li>';
- }
+ <?php
+ foreach ($this->surnameAlpha($tree, $show_marnm === 'yes', $families) as $letter => $count) {
+ echo '<li class="wt-initials-list-item">';
+ if ($count > 0) {
+ echo '<a href="' . e(route($route, [
+ 'alpha' => $letter,
+ 'ged' => $tree->getName(),
+ ])) . '" class="wt-initial' . ($letter === $alpha ? ' active' : '') . '" title="' . I18N::number($count) . '">' . $this->surnameInitial((string)$letter) . '</a>';
+ } else {
+ echo '<span class="wt-initial text-muted">' . $this->surnameInitial((string)$letter) . '</span>';
+ }
+ echo '</li>';
+ }
- // Search spiders don't get the "show all" option as the other links give them everything.
- if (Session::has('initiated')) {
- echo '<li class="wt-initials-list-item">';
- echo '<a class="wt-initial' . ($show_all === 'yes' ? ' active' : '') . '" href="' . e(route($route, ['show_all' => 'yes'] + $params)) . '">';
- echo I18N::translate('All');
- echo '</a>';
- echo '</li>';
- }
- echo '</ul>';
+ // Search spiders don't get the "show all" option as the other links give them everything.
+ if (Session::has('initiated')) {
+ echo '<li class="wt-initials-list-item">';
+ echo '<a class="wt-initial' . ($show_all === 'yes' ? ' active' : '') . '" href="' . e(route($route, ['show_all' => 'yes'] + $params)) . '">';
+ echo I18N::translate('All');
+ echo '</a>';
+ echo '</li>';
+ }
+ echo '</ul>';
- // Search spiders don't get an option to show/hide the surname sublists,
- // nor does it make sense on the all/unknown/surname views
- if (Session::has('initiated') && $show !== 'none') {
- if ($show_marnm === 'yes') {
- echo '<p><a href="', e(route($route, [
- 'show' => $show,
- 'show_marnm' => 'no',
- ] + $params)), '">', I18N::translate('Exclude individuals with “%s” as a married name', $legend), '</a></p>';
- } else {
- echo '<p><a href="', e(route($route, [
- 'show' => $show,
- 'show_marnm' => 'yes',
- ] + $params)), '">', I18N::translate('Include individuals with “%s” as a married name', $legend), '</a></p>';
- }
+ // Search spiders don't get an option to show/hide the surname sublists,
+ // nor does it make sense on the all/unknown/surname views
+ if (Session::has('initiated') && $show !== 'none') {
+ if ($show_marnm === 'yes') {
+ echo '<p><a href="', e(route($route, [
+ 'show' => $show,
+ 'show_marnm' => 'no',
+ ] + $params)), '">', I18N::translate('Exclude individuals with “%s” as a married name', $legend), '</a></p>';
+ } else {
+ echo '<p><a href="', e(route($route, [
+ 'show' => $show,
+ 'show_marnm' => 'yes',
+ ] + $params)), '">', I18N::translate('Include individuals with “%s” as a married name', $legend), '</a></p>';
+ }
- if ($alpha !== '@' && $alpha !== ',' && !$surname) {
- if ($show === 'surn') {
- echo '<p><a href="', e(route($route, [
- 'show' => 'indi',
- 'show_marnm' => 'no',
- ] + $params)), '">', I18N::translate('Show the list of individuals'), '</a></p>';
- } else {
- echo '<p><a href="', e(route($route, [
- 'show' => 'surn',
- 'show_marnm' => 'no',
- ] + $params)), '">', I18N::translate('Show the list of surnames'), '</a></p>';
- }
- }
- }
+ if ($alpha !== '@' && $alpha !== ',' && !$surname) {
+ if ($show === 'surn') {
+ echo '<p><a href="', e(route($route, [
+ 'show' => 'indi',
+ 'show_marnm' => 'no',
+ ] + $params)), '">', I18N::translate('Show the list of individuals'), '</a></p>';
+ } else {
+ echo '<p><a href="', e(route($route, [
+ 'show' => 'surn',
+ 'show_marnm' => 'no',
+ ] + $params)), '">', I18N::translate('Show the list of surnames'), '</a></p>';
+ }
+ }
+ }
- ?>
- </div>
- <div class="wt-page-content">
- <?php
+ ?>
+ </div>
+ <div class="wt-page-content">
+ <?php
- if ($show === 'indi' || $show === 'surn') {
- $surns = $this->surnames($tree, $surname, $alpha, $show_marnm === 'yes', $families);
- if ($show === 'surn') {
- // Show the surname list
- switch ($tree->getPreference('SURNAME_LIST_STYLE')) {
- case 'style1':
- echo FunctionsPrintLists::surnameList($surns, 3, true, $route, $tree);
- break;
- case 'style3':
- echo FunctionsPrintLists::surnameTagCloud($surns, $route, true, $tree);
- break;
- case 'style2':
- default:
- echo view('lists/surnames-table', [
- 'surnames' => $surns,
- 'route' => $route,
- ]);
- break;
- }
- } else {
- // Show the list
- $count = 0;
- foreach ($surns as $surnames) {
- foreach ($surnames as $list) {
- $count += count($list);
- }
- }
- // Don't sublists short lists.
- if ($count < $tree->getPreference('SUBLIST_TRIGGER_I')) {
- $falpha = '';
- } else {
- $givn_initials = $this->givenAlpha($tree, $surname, $alpha, $show_marnm === 'yes', $families);
- // Break long lists by initial letter of given name
- if ($surname || $show_all === 'yes') {
- if ($show_all === 'no') {
- echo '<h2 class="wt-page-title">', I18N::translate('Individuals with surname %s', $legend), '</h2>';
- }
- // Don't show the list until we have some filter criteria
- $show = ($falpha || $show_all_firstnames === 'yes') ? 'indi' : 'none';
- $list = [];
- echo '<ul class="d-flex flex-wrap justify-content-center wt-initials-list">';
- foreach ($givn_initials as $givn_initial => $count) {
- echo '<li class="wt-initials-list-item">';
- if ($count > 0) {
- if ($show === 'indi' && $givn_initial === $falpha && $show_all_firstnames === 'no') {
- echo '<a class="wt-initial active" href="' . e(route($route, ['falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string) $givn_initial) . '</a>';
- } else {
- echo '<a class="wt-initial" href="' . e(route($route, ['falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string) $givn_initial) . '</a>';
- }
- } else {
- echo '<span class="wt-initial text-muted">' . $this->givenNameInitial((string) $givn_initial) . '</span>';
- }
- echo '</li>';
- }
- // Search spiders don't get the "show all" option as the other links give them everything.
- if (Session::has('initiated')) {
- echo '<li class="wt-initials-list-item">';
- if ($show_all_firstnames === 'yes') {
- echo '<span class="wt-initial warning">' . I18N::translate('All') . '</span>';
- } else {
- echo '<a class="wt-initial" href="' . e(route($route, ['show_all_firstnames' => 'yes'] + $params)) . '" title="' . I18N::number($count) . '">' . I18N::translate('All') . '</a>';
- }
- echo '</li>';
- }
- echo '</ul>';
- echo '<p class="center alpha_index">', implode(' | ', $list), '</p>';
- }
- }
- if ($show === 'indi') {
- if ($route === 'individual-list') {
- echo view('lists/individuals-table', [
- 'individuals' => $this->individuals($tree, $surname, $alpha, $falpha, $show_marnm === 'yes', false),
- 'sosa' => false,
- 'tree' => $tree,
- ]);
- } else {
- echo view('lists/families-table', [
- 'families' => $this->families($tree, $surname, $alpha, $falpha, $show_marnm === 'yes'),
- 'tree' => $tree,
- ]);
- }
- }
- }
- }
- ?>
- </div>
- <?php
+ if ($show === 'indi' || $show === 'surn') {
+ $surns = $this->surnames($tree, $surname, $alpha, $show_marnm === 'yes', $families);
+ if ($show === 'surn') {
+ // Show the surname list
+ switch ($tree->getPreference('SURNAME_LIST_STYLE')) {
+ case 'style1':
+ echo FunctionsPrintLists::surnameList($surns, 3, true, $route, $tree);
+ break;
+ case 'style3':
+ echo FunctionsPrintLists::surnameTagCloud($surns, $route, true, $tree);
+ break;
+ case 'style2':
+ default:
+ echo view('lists/surnames-table', [
+ 'surnames' => $surns,
+ 'route' => $route,
+ ]);
+ break;
+ }
+ } else {
+ // Show the list
+ $count = 0;
+ foreach ($surns as $surnames) {
+ foreach ($surnames as $list) {
+ $count += count($list);
+ }
+ }
+ // Don't sublists short lists.
+ if ($count < $tree->getPreference('SUBLIST_TRIGGER_I')) {
+ $falpha = '';
+ } else {
+ $givn_initials = $this->givenAlpha($tree, $surname, $alpha, $show_marnm === 'yes', $families);
+ // Break long lists by initial letter of given name
+ if ($surname || $show_all === 'yes') {
+ if ($show_all === 'no') {
+ echo '<h2 class="wt-page-title">', I18N::translate('Individuals with surname %s', $legend), '</h2>';
+ }
+ // Don't show the list until we have some filter criteria
+ $show = ($falpha || $show_all_firstnames === 'yes') ? 'indi' : 'none';
+ $list = [];
+ echo '<ul class="d-flex flex-wrap justify-content-center wt-initials-list">';
+ foreach ($givn_initials as $givn_initial => $count) {
+ echo '<li class="wt-initials-list-item">';
+ if ($count > 0) {
+ if ($show === 'indi' && $givn_initial === $falpha && $show_all_firstnames === 'no') {
+ echo '<a class="wt-initial active" href="' . e(route($route, ['falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string)$givn_initial) . '</a>';
+ } else {
+ echo '<a class="wt-initial" href="' . e(route($route, ['falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string)$givn_initial) . '</a>';
+ }
+ } else {
+ echo '<span class="wt-initial text-muted">' . $this->givenNameInitial((string)$givn_initial) . '</span>';
+ }
+ echo '</li>';
+ }
+ // Search spiders don't get the "show all" option as the other links give them everything.
+ if (Session::has('initiated')) {
+ echo '<li class="wt-initials-list-item">';
+ if ($show_all_firstnames === 'yes') {
+ echo '<span class="wt-initial warning">' . I18N::translate('All') . '</span>';
+ } else {
+ echo '<a class="wt-initial" href="' . e(route($route, ['show_all_firstnames' => 'yes'] + $params)) . '" title="' . I18N::number($count) . '">' . I18N::translate('All') . '</a>';
+ }
+ echo '</li>';
+ }
+ echo '</ul>';
+ echo '<p class="center alpha_index">', implode(' | ', $list), '</p>';
+ }
+ }
+ if ($show === 'indi') {
+ if ($route === 'individual-list') {
+ echo view('lists/individuals-table', [
+ 'individuals' => $this->individuals($tree, $surname, $alpha, $falpha, $show_marnm === 'yes', false),
+ 'sosa' => false,
+ 'tree' => $tree,
+ ]);
+ } else {
+ echo view('lists/families-table', [
+ 'families' => $this->families($tree, $surname, $alpha, $falpha, $show_marnm === 'yes'),
+ 'tree' => $tree,
+ ]);
+ }
+ }
+ }
+ }
+ ?>
+ </div>
+ <?php
- $html = ob_get_clean();
+ $html = ob_get_clean();
- // @TODO convert this to use views
- return $this->viewResponse('individual-list-page', [
- 'content' => $html,
- 'title' => $title,
- ]);
- }
+ // @TODO convert this to use views
+ return $this->viewResponse('individual-list-page', [
+ 'content' => $html,
+ 'title' => $title,
+ ]);
+ }
- /**
- * Show a list of all media records.
- *
- * @param Request $request
- *
- * @return Response
- */
- public function mediaList(Request $request): Response {
- /** @var Tree $tree */
- $tree = $request->attributes->get('tree');
+ /**
+ * Show a list of all media records.
+ *
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function mediaList(Request $request): Response
+ {
+ /** @var Tree $tree */
+ $tree = $request->attributes->get('tree');
- $formats = GedcomTag::getFileFormTypes();
+ $formats = GedcomTag::getFileFormTypes();
- $action = $request->get('action');
- $page = (int) $request->get('page');
- $max = (int) $request->get('max', 20);
- $folder = $request->get('folder', '');
- $filter = $request->get('filter', '');
- $subdirs = $request->get('subdirs', '1');
- $form_type = $request->get('form_type', '');
+ $action = $request->get('action');
+ $page = (int)$request->get('page');
+ $max = (int)$request->get('max', 20);
+ $folder = $request->get('folder', '');
+ $filter = $request->get('filter', '');
+ $subdirs = $request->get('subdirs', '1');
+ $form_type = $request->get('form_type', '');
- $folders = $this->allFolders($tree);
+ $folders = $this->allFolders($tree);
- if ($action === '1') {
- $media_objects = $this->allMedia(
- $tree,
- $folder,
- $subdirs === '1' ? 'include' : 'exclude',
- 'title',
- $filter,
- $form_type
- );
- } else {
- $media_objects = [];
- }
+ if ($action === '1') {
+ $media_objects = $this->allMedia(
+ $tree,
+ $folder,
+ $subdirs === '1' ? 'include' : 'exclude',
+ 'title',
+ $filter,
+ $form_type
+ );
+ } else {
+ $media_objects = [];
+ }
- // Pagination
- $count = count($media_objects);
- $pages = (int) (($count + $max - 1) / $max);
- $page = max(min($page, $pages), 1);
+ // Pagination
+ $count = count($media_objects);
+ $pages = (int)(($count + $max - 1) / $max);
+ $page = max(min($page, $pages), 1);
- $media_objects = array_slice($media_objects, ($page - 1) * $max, $max);
+ $media_objects = array_slice($media_objects, ($page - 1) * $max, $max);
- return $this->viewResponse('media-list-page', [
- 'count' => $count,
- 'filter' => $filter,
- 'folder' => $folder,
- 'folders' => $folders,
- 'formats' => $formats,
- 'form_type' => $form_type,
- 'max' => $max,
- 'media_objects' => $media_objects,
- 'page' => $page,
- 'pages' => $pages,
- 'subdirs' => $subdirs,
- 'title' => I18N::translate('Media'),
- ]);
- }
+ return $this->viewResponse('media-list-page', [
+ 'count' => $count,
+ 'filter' => $filter,
+ 'folder' => $folder,
+ 'folders' => $folders,
+ 'formats' => $formats,
+ 'form_type' => $form_type,
+ 'max' => $max,
+ 'media_objects' => $media_objects,
+ 'page' => $page,
+ 'pages' => $pages,
+ 'subdirs' => $subdirs,
+ 'title' => I18N::translate('Media'),
+ ]);
+ }
- /**
- * Show a list of all note records.
- *
- * @param Request $request
- *
- * @return Response
- */
- public function noteList(Request $request): Response {
- /** @var Tree $tree */
- $tree = $request->attributes->get('tree');
+ /**
+ * Show a list of all note records.
+ *
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function noteList(Request $request): Response
+ {
+ /** @var Tree $tree */
+ $tree = $request->attributes->get('tree');
- $notes = $this->allNotes($tree);
+ $notes = $this->allNotes($tree);
- return $this->viewResponse('note-list-page', [
- 'notes' => $notes,
- 'title' => I18N::translate('Shared notes'),
- ]);
- }
+ return $this->viewResponse('note-list-page', [
+ 'notes' => $notes,
+ 'title' => I18N::translate('Shared notes'),
+ ]);
+ }
- /**
- * Show a list of all repository records.
- *
- * @param Request $request
- *
- * @return Response
- */
- public function repositoryList(Request $request): Response {
- /** @var Tree $tree */
- $tree = $request->attributes->get('tree');
+ /**
+ * Show a list of all repository records.
+ *
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function repositoryList(Request $request): Response
+ {
+ /** @var Tree $tree */
+ $tree = $request->attributes->get('tree');
- $repositories = $this->allrepositories($tree);
+ $repositories = $this->allrepositories($tree);
- return $this->viewResponse('repository-list-page', [
- 'repositories' => $repositories,
- 'title' => I18N::translate('Repositories'),
- ]);
- }
+ return $this->viewResponse('repository-list-page', [
+ 'repositories' => $repositories,
+ 'title' => I18N::translate('Repositories'),
+ ]);
+ }
- /**
- * Show a list of all source records.
- *
- * @param Request $request
- *
- * @return Response
- */
- public function sourceList(Request $request): Response {
- /** @var Tree $tree */
- $tree = $request->attributes->get('tree');
+ /**
+ * Show a list of all source records.
+ *
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function sourceList(Request $request): Response
+ {
+ /** @var Tree $tree */
+ $tree = $request->attributes->get('tree');
- $sources = $this->allSources($tree);
+ $sources = $this->allSources($tree);
- return $this->viewResponse('source-list-page', [
- 'sources' => $sources,
- 'title' => I18N::translate('Sources'),
- ]);
- }
+ return $this->viewResponse('source-list-page', [
+ 'sources' => $sources,
+ 'title' => I18N::translate('Sources'),
+ ]);
+ }
- /**
- * Generate a list of all the folders in a current tree.
- *
- * @param Tree $tree
- *
- * @return string[]
- */
- private function allFolders(Tree $tree) {
- $folders = Database::prepare(
- "SELECT LEFT(multimedia_file_refn, CHAR_LENGTH(multimedia_file_refn) - CHAR_LENGTH(SUBSTRING_INDEX(multimedia_file_refn, '/', -1))) AS media_path" .
- " FROM `##media_file`" .
- " WHERE m_file = ?" .
- " AND multimedia_file_refn NOT LIKE 'http://%'" .
- " AND multimedia_file_refn NOT LIKE 'https://%'" .
- " GROUP BY 1" .
- " ORDER BY 1"
- )->execute([
- $tree->getTreeId(),
- ])->fetchOneColumn();
+ /**
+ * Generate a list of all the folders in a current tree.
+ *
+ * @param Tree $tree
+ *
+ * @return string[]
+ */
+ private function allFolders(Tree $tree)
+ {
+ $folders = Database::prepare(
+ "SELECT LEFT(multimedia_file_refn, CHAR_LENGTH(multimedia_file_refn) - CHAR_LENGTH(SUBSTRING_INDEX(multimedia_file_refn, '/', -1))) AS media_path" .
+ " FROM `##media_file`" .
+ " WHERE m_file = ?" .
+ " AND multimedia_file_refn NOT LIKE 'http://%'" .
+ " AND multimedia_file_refn NOT LIKE 'https://%'" .
+ " GROUP BY 1" .
+ " ORDER BY 1"
+ )->execute([
+ $tree->getTreeId(),
+ ])->fetchOneColumn();
- // Ensure we have an empty (top level) folder.
- if (!$folders || reset($folders) !== '') {
- array_unshift($folders, '');
- }
+ // Ensure we have an empty (top level) folder.
+ if (!$folders || reset($folders) !== '') {
+ array_unshift($folders, '');
+ }
- return array_combine($folders, $folders);
- }
+ return array_combine($folders, $folders);
+ }
- /**
- * Generate a list of all the media objects matching the criteria in a current tree.
- *
- * @param Tree $tree find media in this tree
- * @param string $folder folder to search
- * @param string $subfolders either "include" or "exclude"
- * @param string $sort either "file" or "title"
- * @param string $filter optional search string
- * @param string $form_type option OBJE/FILE/FORM/TYPE
- *
- * @return Media[]
- */
- private function allMedia(Tree $tree, string $folder, string $subfolders, string $sort, string $filter, string $form_type): array {
- // All files in the folder, plus external files
- $sql =
- "SELECT m_id AS xref, m_gedcom AS gedcom" .
- " FROM `##media`" .
- " JOIN `##media_file` USING (m_id, m_file)" .
- " WHERE m_file = ?";
- $args = [
- $tree->getTreeId(),
- ];
+ /**
+ * Generate a list of all the media objects matching the criteria in a current tree.
+ *
+ * @param Tree $tree find media in this tree
+ * @param string $folder folder to search
+ * @param string $subfolders either "include" or "exclude"
+ * @param string $sort either "file" or "title"
+ * @param string $filter optional search string
+ * @param string $form_type option OBJE/FILE/FORM/TYPE
+ *
+ * @return Media[]
+ */
+ private function allMedia(Tree $tree, string $folder, string $subfolders, string $sort, string $filter, string $form_type): array
+ {
+ // All files in the folder, plus external files
+ $sql =
+ "SELECT m_id AS xref, m_gedcom AS gedcom" .
+ " FROM `##media`" .
+ " JOIN `##media_file` USING (m_id, m_file)" .
+ " WHERE m_file = ?";
+ $args = [
+ $tree->getTreeId(),
+ ];
- // Only show external files when we are looking at the root folder
- if ($folder == '') {
- $sql_external = " OR multimedia_file_refn LIKE 'http://%' OR multimedia_file_refn LIKE 'https://%'";
- } else {
- $sql_external = "";
- }
+ // Only show external files when we are looking at the root folder
+ if ($folder == '') {
+ $sql_external = " OR multimedia_file_refn LIKE 'http://%' OR multimedia_file_refn LIKE 'https://%'";
+ } else {
+ $sql_external = "";
+ }
- // Include / exclude subfolders (but always include external)
- switch ($subfolders) {
- case 'include':
- $sql .= " AND (multimedia_file_refn LIKE CONCAT(?, '%') $sql_external)";
- $args[] = Database::escapeLike($folder);
- break;
- case 'exclude':
- $sql .= " AND (multimedia_file_refn LIKE CONCAT(?, '%') AND multimedia_file_refn NOT LIKE CONCAT(?, '%/%') $sql_external)";
- $args[] = Database::escapeLike($folder);
- $args[] = Database::escapeLike($folder);
- break;
- }
+ // Include / exclude subfolders (but always include external)
+ switch ($subfolders) {
+ case 'include':
+ $sql .= " AND (multimedia_file_refn LIKE CONCAT(?, '%') $sql_external)";
+ $args[] = Database::escapeLike($folder);
+ break;
+ case 'exclude':
+ $sql .= " AND (multimedia_file_refn LIKE CONCAT(?, '%') AND multimedia_file_refn NOT LIKE CONCAT(?, '%/%') $sql_external)";
+ $args[] = Database::escapeLike($folder);
+ $args[] = Database::escapeLike($folder);
+ break;
+ }
- // Apply search terms
- if ($filter) {
- $sql .= " AND (SUBSTRING_INDEX(multimedia_file_refn, '/', -1) LIKE CONCAT('%', ?, '%') OR descriptive_title LIKE CONCAT('%', ?, '%'))";
- $args[] = Database::escapeLike($filter);
- $args[] = Database::escapeLike($filter);
- }
+ // Apply search terms
+ if ($filter) {
+ $sql .= " AND (SUBSTRING_INDEX(multimedia_file_refn, '/', -1) LIKE CONCAT('%', ?, '%') OR descriptive_title LIKE CONCAT('%', ?, '%'))";
+ $args[] = Database::escapeLike($filter);
+ $args[] = Database::escapeLike($filter);
+ }
- if ($form_type) {
- $sql .= " AND source_media_type = ?";
- $args[] = $form_type;
- }
+ if ($form_type) {
+ $sql .= " AND source_media_type = ?";
+ $args[] = $form_type;
+ }
- switch ($sort) {
- case 'file':
- $sql .= " ORDER BY multimedia_file_refn";
- break;
- case 'title':
- $sql .= " ORDER BY descriptive_title";
- break;
- }
+ switch ($sort) {
+ case 'file':
+ $sql .= " ORDER BY multimedia_file_refn";
+ break;
+ case 'title':
+ $sql .= " ORDER BY descriptive_title";
+ break;
+ }
- $rows = Database::prepare($sql)->execute($args)->fetchAll();
- $list = [];
- foreach ($rows as $row) {
- $media = Media::getInstance($row->xref, $tree, $row->gedcom);
- if ($media->canShow()) {
- $list[] = $media;
- }
- }
+ $rows = Database::prepare($sql)->execute($args)->fetchAll();
+ $list = [];
+ foreach ($rows as $row) {
+ $media = Media::getInstance($row->xref, $tree, $row->gedcom);
+ if ($media->canShow()) {
+ $list[] = $media;
+ }
+ }
- return $list;
- }
+ return $list;
+ }
- /**
- * Find all the note records in a tree.
- *
- * @param Tree $tree
- *
- * @return array
- */
- private function allNotes(Tree $tree): array {
- $rows = Database::prepare(
- "SELECT o_id AS xref, o_gedcom AS gedcom FROM `##other` WHERE o_type = 'NOTE' AND o_file = :tree_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ /**
+ * Find all the note records in a tree.
+ *
+ * @param Tree $tree
+ *
+ * @return array
+ */
+ private function allNotes(Tree $tree): array
+ {
+ $rows = Database::prepare(
+ "SELECT o_id AS xref, o_gedcom AS gedcom FROM `##other` WHERE o_type = 'NOTE' AND o_file = :tree_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- $list = [];
- foreach ($rows as $row) {
- $list[] = Note::getInstance($row->xref, $tree, $row->gedcom);
- }
+ $list = [];
+ foreach ($rows as $row) {
+ $list[] = Note::getInstance($row->xref, $tree, $row->gedcom);
+ }
- return array_filter($list, function (Note $x) {
- return $x->canShowName();
- });
- }
+ return array_filter($list, function (Note $x) {
+ return $x->canShowName();
+ });
+ }
- /**
- * Find all the repository record in a tree.
- *
- * @param Tree $tree
- *
- * @return array
- */
- private function allRepositories(Tree $tree): array {
- $rows = Database::prepare(
- "SELECT o_id AS xref, o_gedcom AS gedcom FROM `##other` WHERE o_type = 'REPO' AND o_file = ?"
- )->execute([
- $tree->getTreeId(),
- ])->fetchAll();
+ /**
+ * Find all the repository record in a tree.
+ *
+ * @param Tree $tree
+ *
+ * @return array
+ */
+ private function allRepositories(Tree $tree): array
+ {
+ $rows = Database::prepare(
+ "SELECT o_id AS xref, o_gedcom AS gedcom FROM `##other` WHERE o_type = 'REPO' AND o_file = ?"
+ )->execute([
+ $tree->getTreeId(),
+ ])->fetchAll();
- $list = [];
- foreach ($rows as $row) {
- $list[] = Repository::getInstance($row->xref, $tree, $row->gedcom);
- }
+ $list = [];
+ foreach ($rows as $row) {
+ $list[] = Repository::getInstance($row->xref, $tree, $row->gedcom);
+ }
- return array_filter($list, function (Repository $x) {
- return $x->canShowName();
- });
- }
+ return array_filter($list, function (Repository $x) {
+ return $x->canShowName();
+ });
+ }
- /**
- * Find all the source records in a tree.
- *
- * @param Tree $tree
- *
- * @return array
- */
- private function allSources(Tree $tree): array {
- $rows = Database::prepare(
- "SELECT s_id AS xref, s_gedcom AS gedcom FROM `##sources` WHERE s_file = :tree_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ /**
+ * Find all the source records in a tree.
+ *
+ * @param Tree $tree
+ *
+ * @return array
+ */
+ private function allSources(Tree $tree): array
+ {
+ $rows = Database::prepare(
+ "SELECT s_id AS xref, s_gedcom AS gedcom FROM `##sources` WHERE s_file = :tree_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- $list = [];
- foreach ($rows as $row) {
- $list[] = Source::getInstance($row->xref, $tree, $row->gedcom);
- }
+ $list = [];
+ foreach ($rows as $row) {
+ $list[] = Source::getInstance($row->xref, $tree, $row->gedcom);
+ }
- return array_filter($list, function (Source $x) {
- return $x->canShow();
- });
- }
+ return array_filter($list, function (Source $x) {
+ return $x->canShow();
+ });
+ }
- /**
- * Get a list of initial letters, for lists of names.
- *
- * @param string $locale Return the alphabet for this locale
- *
- * @return string[]
- */
- private function getAlphabetForLocale($locale) {
- switch ($locale) {
- case 'ar':
- return [
- 'ا',
- 'ب',
- 'ت',
- 'ث',
- 'ج',
- 'ح',
- 'خ',
- 'د',
- 'ذ',
- 'ر',
- 'ز',
- 'س',
- 'ش',
- 'ص',
- 'ض',
- 'ط',
- 'ظ',
- 'ع',
- 'غ',
- 'ف',
- 'ق',
- 'ك',
- 'ل',
- 'م',
- 'ن',
- 'ه',
- 'و',
- 'ي',
- 'آ',
- 'ة',
- 'ى',
- 'ی',
- ];
- case 'cs':
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'CH',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- ];
- case 'da':
- case 'nb':
- case 'nn':
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'Æ',
- 'Ø',
- 'Å',
- ];
- case 'el':
- return [
- 'Α',
- 'Β',
- 'Γ',
- 'Δ',
- 'Ε',
- 'Ζ',
- 'Η',
- 'Θ',
- 'Ι',
- 'Κ',
- 'Λ',
- 'Μ',
- 'Ν',
- 'Ξ',
- 'Ο',
- 'Π',
- 'Ρ',
- 'Σ',
- 'Τ',
- 'Υ',
- 'Φ',
- 'Χ',
- 'Ψ',
- 'Ω',
- ];
- case 'es':
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'Ñ',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- ];
- case 'et':
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'Š',
- 'Z',
- 'Ž',
- 'T',
- 'U',
- 'V',
- 'W',
- 'Õ',
- 'Ä',
- 'Ö',
- 'Ü',
- 'X',
- 'Y',
- ];
- case 'fi':
- case 'sv':
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'Å',
- 'Ä',
- 'Ö',
- ];
- case 'he':
- return [
- 'א',
- 'ב',
- 'ג',
- 'ד',
- 'ה',
- 'ו',
- 'ז',
- 'ח',
- 'ט',
- 'י',
- 'כ',
- 'ל',
- 'מ',
- 'נ',
- 'ס',
- 'ע',
- 'פ',
- 'צ',
- 'ק',
- 'ר',
- 'ש',
- 'ת',
- ];
- case 'hu':
- return [
- 'A',
- 'B',
- 'C',
- 'CS',
- 'D',
- 'DZ',
- 'DZS',
- 'E',
- 'F',
- 'G',
- 'GY',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'LY',
- 'M',
- 'N',
- 'NY',
- 'O',
- 'Ö',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'SZ',
- 'T',
- 'TY',
- 'U',
- 'Ü',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'ZS',
- ];
- case 'lt':
- return [
- 'A',
- 'Ą',
- 'B',
- 'C',
- 'Č',
- 'D',
- 'E',
- 'Ę',
- 'Ė',
- 'F',
- 'G',
- 'H',
- 'I',
- 'Y',
- 'Į',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'R',
- 'S',
- 'Š',
- 'T',
- 'U',
- 'Ų',
- 'Ū',
- 'V',
- 'Z',
- 'Ž',
- ];
- case 'nl':
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'IJ',
- ];
- case 'pl':
- return [
- 'A',
- 'B',
- 'C',
- 'Ć',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'Ł',
- 'M',
- 'N',
- 'O',
- 'Ó',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'Ś',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'Ź',
- 'Ż',
- ];
- case 'ro':
- return [
- 'A',
- 'Ă',
- 'Â',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'Î',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'Ş',
- 'T',
- 'Ţ',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- ];
- case 'ru':
- return [
- 'А',
- 'Б',
- 'В',
- 'Г',
- 'Д',
- 'Е',
- 'Ё',
- 'Ж',
- 'З',
- 'И',
- 'Й',
- 'К',
- 'Л',
- 'М',
- 'Н',
- 'О',
- 'П',
- 'Р',
- 'С',
- 'Т',
- 'У',
- 'Ф',
- 'Х',
- 'Ц',
- 'Ч',
- 'Ш',
- 'Щ',
- 'Ъ',
- 'Ы',
- 'Ь',
- 'Э',
- 'Ю',
- 'Я',
- ];
- case 'sk':
- return [
- 'A',
- 'Á',
- 'Ä',
- 'B',
- 'C',
- 'Č',
- 'D',
- 'Ď',
- 'E',
- 'É',
- 'F',
- 'G',
- 'H',
- 'I',
- 'Í',
- 'J',
- 'K',
- 'L',
- 'Ľ',
- 'Ĺ',
- 'M',
- 'N',
- 'Ň',
- 'O',
- 'Ó',
- 'Ô',
- 'P',
- 'Q',
- 'R',
- 'Ŕ',
- 'S',
- 'Š',
- 'T',
- 'Ť',
- 'U',
- 'Ú',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Ý',
- 'Z',
- 'Ž',
- ];
- case 'sl':
- return [
- 'A',
- 'B',
- 'C',
- 'Č',
- 'Ć',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'Š',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'Ž',
- ];
- case 'sr':
- return [
- 'A',
- 'B',
- 'C',
- 'Č',
- 'Ć',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'Š',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- 'Ž',
- ];
- case 'tr':
- return [
- 'A',
- 'B',
- 'C',
- 'Ç',
- 'D',
- 'E',
- 'F',
- 'G',
- 'Ğ',
- 'H',
- 'I',
- 'İ',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'Ö',
- 'P',
- 'R',
- 'S',
- 'Ş',
- 'T',
- 'U',
- 'Ü',
- 'V',
- 'Y',
- 'Z',
- ];
- default:
- return [
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- ];
- }
- }
+ /**
+ * Get a list of initial letters, for lists of names.
+ *
+ * @param string $locale Return the alphabet for this locale
+ *
+ * @return string[]
+ */
+ private function getAlphabetForLocale($locale)
+ {
+ switch ($locale) {
+ case 'ar':
+ return [
+ 'ا',
+ 'ب',
+ 'ت',
+ 'ث',
+ 'ج',
+ 'ح',
+ 'خ',
+ 'د',
+ 'ذ',
+ 'ر',
+ 'ز',
+ 'س',
+ 'ش',
+ 'ص',
+ 'ض',
+ 'ط',
+ 'ظ',
+ 'ع',
+ 'غ',
+ 'ف',
+ 'ق',
+ 'ك',
+ 'ل',
+ 'م',
+ 'ن',
+ 'ه',
+ 'و',
+ 'ي',
+ 'آ',
+ 'ة',
+ 'ى',
+ 'ی',
+ ];
+ case 'cs':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'CH',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ ];
+ case 'da':
+ case 'nb':
+ case 'nn':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'Æ',
+ 'Ø',
+ 'Å',
+ ];
+ case 'el':
+ return [
+ 'Α',
+ 'Β',
+ 'Γ',
+ 'Δ',
+ 'Ε',
+ 'Ζ',
+ 'Η',
+ 'Θ',
+ 'Ι',
+ 'Κ',
+ 'Λ',
+ 'Μ',
+ 'Ν',
+ 'Ξ',
+ 'Ο',
+ 'Π',
+ 'Ρ',
+ 'Σ',
+ 'Τ',
+ 'Υ',
+ 'Φ',
+ 'Χ',
+ 'Ψ',
+ 'Ω',
+ ];
+ case 'es':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'Ñ',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ ];
+ case 'et':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'Š',
+ 'Z',
+ 'Ž',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'Õ',
+ 'Ä',
+ 'Ö',
+ 'Ü',
+ 'X',
+ 'Y',
+ ];
+ case 'fi':
+ case 'sv':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'Å',
+ 'Ä',
+ 'Ö',
+ ];
+ case 'he':
+ return [
+ 'א',
+ 'ב',
+ 'ג',
+ 'ד',
+ 'ה',
+ 'ו',
+ 'ז',
+ 'ח',
+ 'ט',
+ 'י',
+ 'כ',
+ 'ל',
+ 'מ',
+ 'נ',
+ 'ס',
+ 'ע',
+ 'פ',
+ 'צ',
+ 'ק',
+ 'ר',
+ 'ש',
+ 'ת',
+ ];
+ case 'hu':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'CS',
+ 'D',
+ 'DZ',
+ 'DZS',
+ 'E',
+ 'F',
+ 'G',
+ 'GY',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'LY',
+ 'M',
+ 'N',
+ 'NY',
+ 'O',
+ 'Ö',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'SZ',
+ 'T',
+ 'TY',
+ 'U',
+ 'Ü',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'ZS',
+ ];
+ case 'lt':
+ return [
+ 'A',
+ 'Ą',
+ 'B',
+ 'C',
+ 'Č',
+ 'D',
+ 'E',
+ 'Ę',
+ 'Ė',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'Y',
+ 'Į',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'R',
+ 'S',
+ 'Š',
+ 'T',
+ 'U',
+ 'Ų',
+ 'Ū',
+ 'V',
+ 'Z',
+ 'Ž',
+ ];
+ case 'nl':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'IJ',
+ ];
+ case 'pl':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'Ć',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'Ł',
+ 'M',
+ 'N',
+ 'O',
+ 'Ó',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'Ś',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'Ź',
+ 'Ż',
+ ];
+ case 'ro':
+ return [
+ 'A',
+ 'Ă',
+ 'Â',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'Î',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'Ş',
+ 'T',
+ 'Ţ',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ ];
+ case 'ru':
+ return [
+ 'А',
+ 'Б',
+ 'В',
+ 'Г',
+ 'Д',
+ 'Е',
+ 'Ё',
+ 'Ж',
+ 'З',
+ 'И',
+ 'Й',
+ 'К',
+ 'Л',
+ 'М',
+ 'Н',
+ 'О',
+ 'П',
+ 'Р',
+ 'С',
+ 'Т',
+ 'У',
+ 'Ф',
+ 'Х',
+ 'Ц',
+ 'Ч',
+ 'Ш',
+ 'Щ',
+ 'Ъ',
+ 'Ы',
+ 'Ь',
+ 'Э',
+ 'Ю',
+ 'Я',
+ ];
+ case 'sk':
+ return [
+ 'A',
+ 'Á',
+ 'Ä',
+ 'B',
+ 'C',
+ 'Č',
+ 'D',
+ 'Ď',
+ 'E',
+ 'É',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'Í',
+ 'J',
+ 'K',
+ 'L',
+ 'Ľ',
+ 'Ĺ',
+ 'M',
+ 'N',
+ 'Ň',
+ 'O',
+ 'Ó',
+ 'Ô',
+ 'P',
+ 'Q',
+ 'R',
+ 'Ŕ',
+ 'S',
+ 'Š',
+ 'T',
+ 'Ť',
+ 'U',
+ 'Ú',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Ý',
+ 'Z',
+ 'Ž',
+ ];
+ case 'sl':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'Č',
+ 'Ć',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'Š',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'Ž',
+ ];
+ case 'sr':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'Č',
+ 'Ć',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'Š',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ 'Ž',
+ ];
+ case 'tr':
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'Ç',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'Ğ',
+ 'H',
+ 'I',
+ 'İ',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'Ö',
+ 'P',
+ 'R',
+ 'S',
+ 'Ş',
+ 'T',
+ 'U',
+ 'Ü',
+ 'V',
+ 'Y',
+ 'Z',
+ ];
+ default:
+ return [
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ ];
+ }
+ }
- /**
- * Get the initial letter of a name, taking care of multi-letter sequences and equivalences.
- *
- * @param string $name
- *
- * @return string
- */
- public function initialLetter($name) {
- $name = I18N::strtoupper($name);
- switch (WT_LOCALE) {
- case 'cs':
- if (substr($name, 0, 2) == 'CH') {
- return 'CH';
- }
- break;
- case 'da':
- case 'nb':
- case 'nn':
- if (substr($name, 0, 2) == 'AA') {
- return 'Å';
- }
- break;
- case 'hu':
- if (substr($name, 0, 2) == 'CS') {
- return 'CS';
- } elseif (substr($name, 0, 3) == 'DZS') {
- return 'DZS';
- } elseif (substr($name, 0, 2) == 'DZ') {
- return 'DZ';
- } elseif (substr($name, 0, 2) == 'GY') {
- return 'GY';
- } elseif (substr($name, 0, 2) == 'LY') {
- return 'LY';
- } elseif (substr($name, 0, 2) == 'NY') {
- return 'NY';
- } elseif (substr($name, 0, 2) == 'SZ') {
- return 'SZ';
- } elseif (substr($name, 0, 2) == 'TY') {
- return 'TY';
- } elseif (substr($name, 0, 2) == 'ZS') {
- return 'ZS';
- }
- break;
- case 'nl':
- if (substr($name, 0, 2) == 'IJ') {
- return 'IJ';
- }
- break;
- }
+ /**
+ * Get the initial letter of a name, taking care of multi-letter sequences and equivalences.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function initialLetter($name)
+ {
+ $name = I18N::strtoupper($name);
+ switch (WT_LOCALE) {
+ case 'cs':
+ if (substr($name, 0, 2) == 'CH') {
+ return 'CH';
+ }
+ break;
+ case 'da':
+ case 'nb':
+ case 'nn':
+ if (substr($name, 0, 2) == 'AA') {
+ return 'Å';
+ }
+ break;
+ case 'hu':
+ if (substr($name, 0, 2) == 'CS') {
+ return 'CS';
+ } elseif (substr($name, 0, 3) == 'DZS') {
+ return 'DZS';
+ } elseif (substr($name, 0, 2) == 'DZ') {
+ return 'DZ';
+ } elseif (substr($name, 0, 2) == 'GY') {
+ return 'GY';
+ } elseif (substr($name, 0, 2) == 'LY') {
+ return 'LY';
+ } elseif (substr($name, 0, 2) == 'NY') {
+ return 'NY';
+ } elseif (substr($name, 0, 2) == 'SZ') {
+ return 'SZ';
+ } elseif (substr($name, 0, 2) == 'TY') {
+ return 'TY';
+ } elseif (substr($name, 0, 2) == 'ZS') {
+ return 'ZS';
+ }
+ break;
+ case 'nl':
+ if (substr($name, 0, 2) == 'IJ') {
+ return 'IJ';
+ }
+ break;
+ }
- // No special rules - just take the first character
- return mb_substr($name, 0, 1);
- }
+ // No special rules - just take the first character
+ return mb_substr($name, 0, 1);
+ }
- /**
- * Generate SQL to match a given letter, taking care of cases that
- * are not covered by the collation setting.
- *
- * We must consider:
- * potential substrings, such as Czech "CH" and "C"
- * equivalent letters, such as Danish "AA" and "Å"
- *
- * We COULD write something that handles all languages generically,
- * but its performance would most likely be poor.
- *
- * For languages that don't appear in this list, we could write
- * simpler versions of the surnameAlpha() and givenAlpha() functions,
- * but it gives no noticable improvement in performance.
- *
- * @param string $field
- * @param string $letter
- *
- * @return string
- */
- private function getInitialSql($field, $letter) {
- switch (WT_LOCALE) {
- case 'cs':
- switch ($letter) {
- case 'C':
- return $field . " LIKE 'C%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'CH%' COLLATE " . I18N::collation();
- }
- break;
- case 'da':
- case 'nb':
- case 'nn':
- switch ($letter) {
- // AA gets listed under Å
- case 'A':
- return $field . " LIKE 'A%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'AA%' COLLATE " . I18N::collation();
- case 'Å':
- return "(" . $field . " LIKE 'Å%' COLLATE " . I18N::collation() . " OR " . $field . " LIKE 'AA%' COLLATE " . I18N::collation() . ")";
- }
- break;
- case 'hu':
- switch ($letter) {
- case 'C':
- return $field . " LIKE 'C%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'CS%' COLLATE " . I18N::collation();
- case 'D':
- return $field . " LIKE 'D%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'DZ%' COLLATE " . I18N::collation();
- case 'DZ':
- return $field . " LIKE 'DZ%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'DZS%' COLLATE " . I18N::collation();
- case 'G':
- return $field . " LIKE 'G%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'GY%' COLLATE " . I18N::collation();
- case 'L':
- return $field . " LIKE 'L%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'LY%' COLLATE " . I18N::collation();
- case 'N':
- return $field . " LIKE 'N%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'NY%' COLLATE " . I18N::collation();
- case 'S':
- return $field . " LIKE 'S%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'SZ%' COLLATE " . I18N::collation();
- case 'T':
- return $field . " LIKE 'T%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'TY%' COLLATE " . I18N::collation();
- case 'Z':
- return $field . " LIKE 'Z%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'ZS%' COLLATE " . I18N::collation();
- }
- break;
- case 'nl':
- switch ($letter) {
- case 'I':
- return $field . " LIKE 'I%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'IJ%' COLLATE " . I18N::collation();
- }
- break;
- }
+ /**
+ * Generate SQL to match a given letter, taking care of cases that
+ * are not covered by the collation setting.
+ *
+ * We must consider:
+ * potential substrings, such as Czech "CH" and "C"
+ * equivalent letters, such as Danish "AA" and "Å"
+ *
+ * We COULD write something that handles all languages generically,
+ * but its performance would most likely be poor.
+ *
+ * For languages that don't appear in this list, we could write
+ * simpler versions of the surnameAlpha() and givenAlpha() functions,
+ * but it gives no noticable improvement in performance.
+ *
+ * @param string $field
+ * @param string $letter
+ *
+ * @return string
+ */
+ private function getInitialSql($field, $letter)
+ {
+ switch (WT_LOCALE) {
+ case 'cs':
+ switch ($letter) {
+ case 'C':
+ return $field . " LIKE 'C%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'CH%' COLLATE " . I18N::collation();
+ }
+ break;
+ case 'da':
+ case 'nb':
+ case 'nn':
+ switch ($letter) {
+ // AA gets listed under Å
+ case 'A':
+ return $field . " LIKE 'A%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'AA%' COLLATE " . I18N::collation();
+ case 'Å':
+ return "(" . $field . " LIKE 'Å%' COLLATE " . I18N::collation() . " OR " . $field . " LIKE 'AA%' COLLATE " . I18N::collation() . ")";
+ }
+ break;
+ case 'hu':
+ switch ($letter) {
+ case 'C':
+ return $field . " LIKE 'C%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'CS%' COLLATE " . I18N::collation();
+ case 'D':
+ return $field . " LIKE 'D%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'DZ%' COLLATE " . I18N::collation();
+ case 'DZ':
+ return $field . " LIKE 'DZ%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'DZS%' COLLATE " . I18N::collation();
+ case 'G':
+ return $field . " LIKE 'G%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'GY%' COLLATE " . I18N::collation();
+ case 'L':
+ return $field . " LIKE 'L%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'LY%' COLLATE " . I18N::collation();
+ case 'N':
+ return $field . " LIKE 'N%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'NY%' COLLATE " . I18N::collation();
+ case 'S':
+ return $field . " LIKE 'S%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'SZ%' COLLATE " . I18N::collation();
+ case 'T':
+ return $field . " LIKE 'T%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'TY%' COLLATE " . I18N::collation();
+ case 'Z':
+ return $field . " LIKE 'Z%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'ZS%' COLLATE " . I18N::collation();
+ }
+ break;
+ case 'nl':
+ switch ($letter) {
+ case 'I':
+ return $field . " LIKE 'I%' COLLATE " . I18N::collation() . " AND " . $field . " NOT LIKE 'IJ%' COLLATE " . I18N::collation();
+ }
+ break;
+ }
- // Easy cases: the MySQL collation rules take care of it
- return "$field LIKE CONCAT('@'," . Database::quote($letter) . ",'%') COLLATE " . I18N::collation() . " ESCAPE '@'";
- }
+ // Easy cases: the MySQL collation rules take care of it
+ return "$field LIKE CONCAT('@'," . Database::quote($letter) . ",'%') COLLATE " . I18N::collation() . " ESCAPE '@'";
+ }
- /**
- * Get a list of initial surname letters for indilist.php and famlist.php
- *
- * @param Tree $tree
- * @param bool $marnm if set, include married names
- * @param bool $fams if set, only consider individuals with FAMS records
- * @param bool $totals if set, count the number of names beginning with each letter
- *
- * @return int[]
- */
- public function surnameAlpha(Tree $tree, $marnm, $fams, $totals = true) {
- $alphas = [];
+ /**
+ * Get a list of initial surname letters for indilist.php and famlist.php
+ *
+ * @param Tree $tree
+ * @param bool $marnm if set, include married names
+ * @param bool $fams if set, only consider individuals with FAMS records
+ * @param bool $totals if set, count the number of names beginning with each letter
+ *
+ * @return int[]
+ */
+ public function surnameAlpha(Tree $tree, $marnm, $fams, $totals = true)
+ {
+ $alphas = [];
- $sql =
- "SELECT COUNT(n_id)" .
- " FROM `##name` " .
- ($fams ? " JOIN `##link` ON (n_id=l_from AND n_file=l_file AND l_type='FAMS') " : "") .
- " WHERE n_file=" . $tree->getTreeId() .
- ($marnm ? "" : " AND n_type!='_MARNM'");
+ $sql =
+ "SELECT COUNT(n_id)" .
+ " FROM `##name` " .
+ ($fams ? " JOIN `##link` ON (n_id=l_from AND n_file=l_file AND l_type='FAMS') " : "") .
+ " WHERE n_file=" . $tree->getTreeId() .
+ ($marnm ? "" : " AND n_type!='_MARNM'");
- // Fetch all the letters in our alphabet, whether or not there
- // are any names beginning with that letter. It looks better to
- // show the full alphabet, rather than omitting rare letters such as X
- foreach ($this->getAlphabetForLocale(WT_LOCALE) as $letter) {
- $count = 1;
- if ($totals) {
- $count = Database::prepare($sql . " AND " . $this->getInitialSql('n_surn', $letter))->fetchOne();
- }
- $alphas[$letter] = $count;
- }
+ // Fetch all the letters in our alphabet, whether or not there
+ // are any names beginning with that letter. It looks better to
+ // show the full alphabet, rather than omitting rare letters such as X
+ foreach ($this->getAlphabetForLocale(WT_LOCALE) as $letter) {
+ $count = 1;
+ if ($totals) {
+ $count = Database::prepare($sql . " AND " . $this->getInitialSql('n_surn', $letter))->fetchOne();
+ }
+ $alphas[$letter] = $count;
+ }
- // Now fetch initial letters that are not in our alphabet,
- // including "@" (for "@N.N.") and "" for no surname.
- $sql =
- "SELECT initial, count FROM (SELECT UPPER(LEFT(n_surn, 1)) AS initial, COUNT(n_id) AS count" .
- " FROM `##name` " .
- ($fams ? " JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
- " WHERE n_file = :tree_id AND n_surn <> ''" .
- ($marnm ? "" : " AND n_type != '_MARNM'");
+ // Now fetch initial letters that are not in our alphabet,
+ // including "@" (for "@N.N.") and "" for no surname.
+ $sql =
+ "SELECT initial, count FROM (SELECT UPPER(LEFT(n_surn, 1)) AS initial, COUNT(n_id) AS count" .
+ " FROM `##name` " .
+ ($fams ? " JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
+ " WHERE n_file = :tree_id AND n_surn <> ''" .
+ ($marnm ? "" : " AND n_type != '_MARNM'");
- $args = [
- 'tree_id' => $tree->getTreeId(),
- ];
+ $args = [
+ 'tree_id' => $tree->getTreeId(),
+ ];
- foreach ($this->getAlphabetForLocale(WT_LOCALE) as $n => $letter) {
- $sql .= " AND n_surn COLLATE :collate_" . $n . " NOT LIKE :letter_" . $n;
- $args['collate_' . $n] = I18N::collation();
- $args['letter_' . $n] = $letter . '%';
- }
- $sql .= " GROUP BY UPPER(LEFT(n_surn, 1))) AS subquery ORDER BY initial = '', initial = '@', initial";
- foreach (Database::prepare($sql)->execute($args)->fetchAssoc() as $alpha => $count) {
- $alphas[$alpha] = $count;
- }
+ foreach ($this->getAlphabetForLocale(WT_LOCALE) as $n => $letter) {
+ $sql .= " AND n_surn COLLATE :collate_" . $n . " NOT LIKE :letter_" . $n;
+ $args['collate_' . $n] = I18N::collation();
+ $args['letter_' . $n] = $letter . '%';
+ }
+ $sql .= " GROUP BY UPPER(LEFT(n_surn, 1))) AS subquery ORDER BY initial = '', initial = '@', initial";
+ foreach (Database::prepare($sql)->execute($args)->fetchAssoc() as $alpha => $count) {
+ $alphas[$alpha] = $count;
+ }
- // Names with no surname
- $sql =
- "SELECT COUNT(n_id)" .
- " FROM `##name` " .
- ($fams ? " JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
- " WHERE n_file = :tree_id AND n_surn = ''" .
- ($marnm ? "" : " AND n_type != '_MARNM'");
+ // Names with no surname
+ $sql =
+ "SELECT COUNT(n_id)" .
+ " FROM `##name` " .
+ ($fams ? " JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
+ " WHERE n_file = :tree_id AND n_surn = ''" .
+ ($marnm ? "" : " AND n_type != '_MARNM'");
- $args = [
- 'tree_id' => $tree->getTreeId(),
- ];
+ $args = [
+ 'tree_id' => $tree->getTreeId(),
+ ];
- $count_no_surname = (int) Database::prepare($sql)->execute($args)->fetchOne();
- if ($count_no_surname !== 0) {
- // Special code to indicate "no surname"
- $alphas[','] = $count_no_surname;
- }
+ $count_no_surname = (int)Database::prepare($sql)->execute($args)->fetchOne();
+ if ($count_no_surname !== 0) {
+ // Special code to indicate "no surname"
+ $alphas[','] = $count_no_surname;
+ }
- return $alphas;
- }
+ return $alphas;
+ }
- /**
- * Get a list of initial given name letters for indilist.php and famlist.php
- *
- * @param Tree $tree
- * @param string $surn if set, only consider people with this surname
- * @param string $salpha if set, only consider surnames starting with this letter
- * @param bool $marnm if set, include married names
- * @param bool $fams if set, only consider individuals with FAMS records
- *
- * @return int[]
- */
- public function givenAlpha(Tree $tree, $surn, $salpha, $marnm, $fams) {
- $alphas = [];
+ /**
+ * Get a list of initial given name letters for indilist.php and famlist.php
+ *
+ * @param Tree $tree
+ * @param string $surn if set, only consider people with this surname
+ * @param string $salpha if set, only consider surnames starting with this letter
+ * @param bool $marnm if set, include married names
+ * @param bool $fams if set, only consider individuals with FAMS records
+ *
+ * @return int[]
+ */
+ public function givenAlpha(Tree $tree, $surn, $salpha, $marnm, $fams)
+ {
+ $alphas = [];
- $sql =
- "SELECT COUNT(DISTINCT n_id)" .
- " FROM `##name`" .
- ($fams ? " JOIN `##link` ON (n_id=l_from AND n_file=l_file AND l_type='FAMS') " : "") .
- " WHERE n_file=" . $tree->getTreeId() . " " .
- ($marnm ? "" : " AND n_type!='_MARNM'");
+ $sql =
+ "SELECT COUNT(DISTINCT n_id)" .
+ " FROM `##name`" .
+ ($fams ? " JOIN `##link` ON (n_id=l_from AND n_file=l_file AND l_type='FAMS') " : "") .
+ " WHERE n_file=" . $tree->getTreeId() . " " .
+ ($marnm ? "" : " AND n_type!='_MARNM'");
- if ($surn) {
- $sql .= " AND n_surn=" . Database::quote($surn) . " COLLATE '" . I18N::collation() . "'";
- } elseif ($salpha == ',') {
- $sql .= " AND n_surn=''";
- } elseif ($salpha == '@') {
- $sql .= " AND n_surn='@N.N.'";
- } elseif ($salpha) {
- $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
- } else {
- // All surnames
- $sql .= " AND n_surn NOT IN ('', '@N.N.')";
- }
+ if ($surn) {
+ $sql .= " AND n_surn=" . Database::quote($surn) . " COLLATE '" . I18N::collation() . "'";
+ } elseif ($salpha == ',') {
+ $sql .= " AND n_surn=''";
+ } elseif ($salpha == '@') {
+ $sql .= " AND n_surn='@N.N.'";
+ } elseif ($salpha) {
+ $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
+ } else {
+ // All surnames
+ $sql .= " AND n_surn NOT IN ('', '@N.N.')";
+ }
- // Fetch all the letters in our alphabet, whether or not there
- // are any names beginning with that letter. It looks better to
- // show the full alphabet, rather than omitting rare letters such as X
- foreach ($this->getAlphabetForLocale(WT_LOCALE) as $letter) {
- $count = Database::prepare($sql . " AND " . $this->getInitialSql('n_givn', $letter))->fetchOne();
- $alphas[$letter] = $count;
- }
+ // Fetch all the letters in our alphabet, whether or not there
+ // are any names beginning with that letter. It looks better to
+ // show the full alphabet, rather than omitting rare letters such as X
+ foreach ($this->getAlphabetForLocale(WT_LOCALE) as $letter) {
+ $count = Database::prepare($sql . " AND " . $this->getInitialSql('n_givn', $letter))->fetchOne();
+ $alphas[$letter] = $count;
+ }
- // Now fetch initial letters that are not in our alphabet,
- // including "@" (for "@N.N.") and "" for no surname
- $sql =
- "SELECT initial, total FROM (SELECT UPPER(LEFT(n_givn, 1)) AS initial, COUNT(DISTINCT n_id) AS total" .
- " FROM `##name` " .
- ($fams ? " JOIN `##link` ON (n_id = l_from AND n_file = l_file AND l_type = 'FAMS') " : "") .
- " WHERE n_file = :tree_id" .
- ($marnm ? "" : " AND n_type != '_MARNM'");
+ // Now fetch initial letters that are not in our alphabet,
+ // including "@" (for "@N.N.") and "" for no surname
+ $sql =
+ "SELECT initial, total FROM (SELECT UPPER(LEFT(n_givn, 1)) AS initial, COUNT(DISTINCT n_id) AS total" .
+ " FROM `##name` " .
+ ($fams ? " JOIN `##link` ON (n_id = l_from AND n_file = l_file AND l_type = 'FAMS') " : "") .
+ " WHERE n_file = :tree_id" .
+ ($marnm ? "" : " AND n_type != '_MARNM'");
- $args = [
- 'tree_id' => $tree->getTreeId(),
- ];
+ $args = [
+ 'tree_id' => $tree->getTreeId(),
+ ];
- if ($surn) {
- $sql .= " AND n_surn COLLATE :collate_1 = :surn";
- $args['collate_1'] = I18N::collation();
- $args['surn'] = $surn;
- } elseif ($salpha === ',') {
- $sql .= " AND n_surn = ''";
- } elseif ($salpha === '@') {
- $sql .= " AND n_surn = '@N.N.'";
- } elseif ($salpha) {
- $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
- } else {
- // All surnames
- $sql .= " AND n_surn NOT IN ('', '@N.N.')";
- }
+ if ($surn) {
+ $sql .= " AND n_surn COLLATE :collate_1 = :surn";
+ $args['collate_1'] = I18N::collation();
+ $args['surn'] = $surn;
+ } elseif ($salpha === ',') {
+ $sql .= " AND n_surn = ''";
+ } elseif ($salpha === '@') {
+ $sql .= " AND n_surn = '@N.N.'";
+ } elseif ($salpha) {
+ $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
+ } else {
+ // All surnames
+ $sql .= " AND n_surn NOT IN ('', '@N.N.')";
+ }
- foreach ($this->getAlphabetForLocale(WT_LOCALE) as $letter) {
- $sql .= " AND n_givn NOT LIKE '" . $letter . "%' COLLATE " . I18N::collation();
- }
- $sql .= " GROUP BY UPPER(LEFT(n_givn, 1))) AS subquery ORDER BY initial = '@', initial = '', initial";
+ foreach ($this->getAlphabetForLocale(WT_LOCALE) as $letter) {
+ $sql .= " AND n_givn NOT LIKE '" . $letter . "%' COLLATE " . I18N::collation();
+ }
+ $sql .= " GROUP BY UPPER(LEFT(n_givn, 1))) AS subquery ORDER BY initial = '@', initial = '', initial";
- foreach (Database::prepare($sql)->execute($args)->fetchAssoc() as $alpha => $count) {
- $alphas[$alpha] = $count;
- }
+ foreach (Database::prepare($sql)->execute($args)->fetchAssoc() as $alpha => $count) {
+ $alphas[$alpha] = $count;
+ }
- return $alphas;
- }
+ return $alphas;
+ }
- /**
- * Get a list of actual surnames and variants, based on a "root" surname.
- *
- * @param Tree $tree
- * @param string $surn if set, only fetch people with this surname
- * @param string $salpha if set, only consider surnames starting with this letter
- * @param bool $marnm if set, include married names
- * @param bool $fams if set, only consider individuals with FAMS records
- *
- * @return array
- */
- public function surnames(Tree $tree, $surn, $salpha, $marnm, $fams) {
- $sql =
- "SELECT n2.n_surn, n1.n_surname, n1.n_id" .
- " FROM `##name` n1 " .
- ($fams ? " JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
- " JOIN (SELECT n_surn COLLATE :collate_0 AS n_surn, n_file FROM `##name`" .
- " WHERE n_file = :tree_id" .
- ($marnm ? "" : " AND n_type != '_MARNM'");
+ /**
+ * Get a list of actual surnames and variants, based on a "root" surname.
+ *
+ * @param Tree $tree
+ * @param string $surn if set, only fetch people with this surname
+ * @param string $salpha if set, only consider surnames starting with this letter
+ * @param bool $marnm if set, include married names
+ * @param bool $fams if set, only consider individuals with FAMS records
+ *
+ * @return array
+ */
+ public function surnames(Tree $tree, $surn, $salpha, $marnm, $fams)
+ {
+ $sql =
+ "SELECT n2.n_surn, n1.n_surname, n1.n_id" .
+ " FROM `##name` n1 " .
+ ($fams ? " JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
+ " JOIN (SELECT n_surn COLLATE :collate_0 AS n_surn, n_file FROM `##name`" .
+ " WHERE n_file = :tree_id" .
+ ($marnm ? "" : " AND n_type != '_MARNM'");
- $args = [
- 'tree_id' => $tree->getTreeId(),
- 'collate_0' => I18N::collation(),
- ];
+ $args = [
+ 'tree_id' => $tree->getTreeId(),
+ 'collate_0' => I18N::collation(),
+ ];
- if ($surn) {
- $sql .= " AND n_surn COLLATE :collate_1 = :surn";
- $args['collate_1'] = I18N::collation();
- $args['surn'] = $surn;
- } elseif ($salpha === ',') {
- $sql .= " AND n_surn = ''";
- } elseif ($salpha === '@') {
- $sql .= " AND n_surn = '@N.N.'";
- } elseif ($salpha) {
- $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
- } else {
- // All surnames
- $sql .= " AND n_surn NOT IN ('', '@N.N.')";
- }
- $sql .= " GROUP BY n_surn COLLATE :collate_2, n_file) AS n2 ON (n1.n_surn = n2.n_surn COLLATE :collate_3 AND n1.n_file = n2.n_file)";
- $args['collate_2'] = I18N::collation();
- $args['collate_3'] = I18N::collation();
+ if ($surn) {
+ $sql .= " AND n_surn COLLATE :collate_1 = :surn";
+ $args['collate_1'] = I18N::collation();
+ $args['surn'] = $surn;
+ } elseif ($salpha === ',') {
+ $sql .= " AND n_surn = ''";
+ } elseif ($salpha === '@') {
+ $sql .= " AND n_surn = '@N.N.'";
+ } elseif ($salpha) {
+ $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
+ } else {
+ // All surnames
+ $sql .= " AND n_surn NOT IN ('', '@N.N.')";
+ }
+ $sql .= " GROUP BY n_surn COLLATE :collate_2, n_file) AS n2 ON (n1.n_surn = n2.n_surn COLLATE :collate_3 AND n1.n_file = n2.n_file)";
+ $args['collate_2'] = I18N::collation();
+ $args['collate_3'] = I18N::collation();
- $list = [];
- foreach (Database::prepare($sql)->execute($args)->fetchAll() as $row) {
- $list[I18N::strtoupper($row->n_surn)][$row->n_surname][$row->n_id] = true;
- }
+ $list = [];
+ foreach (Database::prepare($sql)->execute($args)->fetchAll() as $row) {
+ $list[I18N::strtoupper($row->n_surn)][$row->n_surname][$row->n_id] = true;
+ }
- return $list;
- }
+ return $list;
+ }
- /**
- * Fetch a list of individuals with specified names
- *
- * To search for unknown names, use $surn="@N.N.", $salpha="@" or $galpha="@"
- * To search for names with no surnames, use $salpha=","
- *
- * @param Tree $tree
- * @param string $surn if set, only fetch people with this surname
- * @param string $salpha if set, only fetch surnames starting with this letter
- * @param string $galpha if set, only fetch given names starting with this letter
- * @param bool $marnm if set, include married names
- * @param bool $fams if set, only fetch individuals with FAMS records
- *
- * @return Individual[]
- */
- private function individuals(Tree $tree, $surn, $salpha, $galpha, $marnm, $fams) {
- $sql =
- "SELECT i_id AS xref, i_gedcom AS gedcom, n_full " .
- "FROM `##individuals` " .
- "JOIN `##name` ON n_id = i_id AND n_file = i_file " .
- ($fams ? "JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
- "WHERE n_file = :tree_id " .
- ($marnm ? "" : "AND n_type != '_MARNM'");
+ /**
+ * Fetch a list of individuals with specified names
+ *
+ * To search for unknown names, use $surn="@N.N.", $salpha="@" or $galpha="@"
+ * To search for names with no surnames, use $salpha=","
+ *
+ * @param Tree $tree
+ * @param string $surn if set, only fetch people with this surname
+ * @param string $salpha if set, only fetch surnames starting with this letter
+ * @param string $galpha if set, only fetch given names starting with this letter
+ * @param bool $marnm if set, include married names
+ * @param bool $fams if set, only fetch individuals with FAMS records
+ *
+ * @return Individual[]
+ */
+ private function individuals(Tree $tree, $surn, $salpha, $galpha, $marnm, $fams)
+ {
+ $sql =
+ "SELECT i_id AS xref, i_gedcom AS gedcom, n_full " .
+ "FROM `##individuals` " .
+ "JOIN `##name` ON n_id = i_id AND n_file = i_file " .
+ ($fams ? "JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") .
+ "WHERE n_file = :tree_id " .
+ ($marnm ? "" : "AND n_type != '_MARNM'");
- $args = [
- 'tree_id' => $tree->getTreeId(),
- ];
+ $args = [
+ 'tree_id' => $tree->getTreeId(),
+ ];
- if ($surn) {
- $sql .= " AND n_surn COLLATE :collate_1 = :surn";
- $args['collate_1'] = I18N::collation();
- $args['surn'] = $surn;
- } elseif ($salpha === ',') {
- $sql .= " AND n_surn = ''";
- } elseif ($salpha === '@') {
- $sql .= " AND n_surn = '@N.N.'";
- } elseif ($salpha) {
- $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
- } else {
- // All surnames
- $sql .= " AND n_surn NOT IN ('', '@N.N.')";
- }
- if ($galpha) {
- $sql .= " AND " . $this->getInitialSql('n_givn', $galpha);
- }
+ if ($surn) {
+ $sql .= " AND n_surn COLLATE :collate_1 = :surn";
+ $args['collate_1'] = I18N::collation();
+ $args['surn'] = $surn;
+ } elseif ($salpha === ',') {
+ $sql .= " AND n_surn = ''";
+ } elseif ($salpha === '@') {
+ $sql .= " AND n_surn = '@N.N.'";
+ } elseif ($salpha) {
+ $sql .= " AND " . $this->getInitialSql('n_surn', $salpha);
+ } else {
+ // All surnames
+ $sql .= " AND n_surn NOT IN ('', '@N.N.')";
+ }
+ if ($galpha) {
+ $sql .= " AND " . $this->getInitialSql('n_givn', $galpha);
+ }
- $sql .= " ORDER BY CASE n_surn WHEN '@N.N.' THEN 1 ELSE 0 END, n_surn COLLATE :collate_2, CASE n_givn WHEN '@P.N.' THEN 1 ELSE 0 END, n_givn COLLATE :collate_3";
- $args['collate_2'] = I18N::collation();
- $args['collate_3'] = I18N::collation();
+ $sql .= " ORDER BY CASE n_surn WHEN '@N.N.' THEN 1 ELSE 0 END, n_surn COLLATE :collate_2, CASE n_givn WHEN '@P.N.' THEN 1 ELSE 0 END, n_givn COLLATE :collate_3";
+ $args['collate_2'] = I18N::collation();
+ $args['collate_3'] = I18N::collation();
- $list = [];
- $rows = Database::prepare($sql)->execute($args)->fetchAll();
- foreach ($rows as $row) {
- $person = Individual::getInstance($row->xref, $tree, $row->gedcom);
- // The name from the database may be private - check the filtered list...
- foreach ($person->getAllNames() as $n => $name) {
- if ($name['fullNN'] == $row->n_full) {
- $person->setPrimaryName($n);
- // We need to clone $person, as we may have multiple references to the
- // same person in this list, and the "primary name" would otherwise
- // be shared amongst all of them.
- $list[] = clone $person;
- break;
- }
- }
- }
+ $list = [];
+ $rows = Database::prepare($sql)->execute($args)->fetchAll();
+ foreach ($rows as $row) {
+ $person = Individual::getInstance($row->xref, $tree, $row->gedcom);
+ // The name from the database may be private - check the filtered list...
+ foreach ($person->getAllNames() as $n => $name) {
+ if ($name['fullNN'] == $row->n_full) {
+ $person->setPrimaryName($n);
+ // We need to clone $person, as we may have multiple references to the
+ // same person in this list, and the "primary name" would otherwise
+ // be shared amongst all of them.
+ $list[] = clone $person;
+ break;
+ }
+ }
+ }
- return $list;
- }
+ return $list;
+ }
- /**
- * Fetch a list of families with specified names
- *
- * To search for unknown names, use $surn="@N.N.", $salpha="@" or $galpha="@"
- * To search for names with no surnames, use $salpha=","
- *
- * @param Tree $tree
- * @param string $surn if set, only fetch people with this surname
- * @param string $salpha if set, only fetch surnames starting with this letter
- * @param string $galpha if set, only fetch given names starting with this letter
- * @param bool $marnm if set, include married names
- *
- * @return Family[]
- */
- private function families(Tree $tree, $surn, $salpha, $galpha, $marnm) {
- $list = [];
- foreach ($this->individuals($tree, $surn, $salpha, $galpha, $marnm, true) as $indi) {
- foreach ($indi->getSpouseFamilies() as $family) {
- $list[$family->getXref()] = $family;
- }
- }
- usort($list, '\Fisharebest\Webtrees\GedcomRecord::compare');
+ /**
+ * Fetch a list of families with specified names
+ *
+ * To search for unknown names, use $surn="@N.N.", $salpha="@" or $galpha="@"
+ * To search for names with no surnames, use $salpha=","
+ *
+ * @param Tree $tree
+ * @param string $surn if set, only fetch people with this surname
+ * @param string $salpha if set, only fetch surnames starting with this letter
+ * @param string $galpha if set, only fetch given names starting with this letter
+ * @param bool $marnm if set, include married names
+ *
+ * @return Family[]
+ */
+ private function families(Tree $tree, $surn, $salpha, $galpha, $marnm)
+ {
+ $list = [];
+ foreach ($this->individuals($tree, $surn, $salpha, $galpha, $marnm, true) as $indi) {
+ foreach ($indi->getSpouseFamilies() as $family) {
+ $list[$family->getXref()] = $family;
+ }
+ }
+ usort($list, '\Fisharebest\Webtrees\GedcomRecord::compare');
- return $list;
- }
+ return $list;
+ }
- /**
- * Some initial letters have a special meaning
- *
- * @param string $initial
- *
- * @return string
- */
- public function givenNameInitial(string $initial): string {
- switch ($initial) {
- case '@':
- return I18N::translateContext('Unknown given name', '…');
- default:
- return e($initial);
- }
- }
+ /**
+ * Some initial letters have a special meaning
+ *
+ * @param string $initial
+ *
+ * @return string
+ */
+ public function givenNameInitial(string $initial): string
+ {
+ switch ($initial) {
+ case '@':
+ return I18N::translateContext('Unknown given name', '…');
+ default:
+ return e($initial);
+ }
+ }
- /**
- * Some initial letters have a special meaning
- *
- * @param string $initial
- *
- * @return string
- */
- public function surnameInitial(string $initial): string {
- switch ($initial) {
- case '@':
- return I18N::translateContext('Unknown surname', '…');
- case ',':
- return I18N::translate('None');
- default:
- return e($initial);
- }
- }
+ /**
+ * Some initial letters have a special meaning
+ *
+ * @param string $initial
+ *
+ * @return string
+ */
+ public function surnameInitial(string $initial): string
+ {
+ switch ($initial) {
+ case '@':
+ return I18N::translateContext('Unknown surname', '…');
+ case ',':
+ return I18N::translate('None');
+ default:
+ return e($initial);
+ }
+ }
}