diff options
Diffstat (limited to 'app/Http/Controllers/IndividualController.php')
| -rw-r--r-- | app/Http/Controllers/IndividualController.php | 702 |
1 files changed, 359 insertions, 343 deletions
diff --git a/app/Http/Controllers/IndividualController.php b/app/Http/Controllers/IndividualController.php index 97ba2f3005..6ce1a5425f 100644 --- a/app/Http/Controllers/IndividualController.php +++ b/app/Http/Controllers/IndividualController.php @@ -42,291 +42,300 @@ use Symfony\Component\HttpFoundation\Response; /** * Controller for the individual page. */ -class IndividualController extends AbstractBaseController { - // Do not show these facts in the expanded chart boxes. - const EXCLUDE_CHART_FACTS = [ - 'ADDR', - 'ALIA', - 'ASSO', - 'CHAN', - 'CHIL', - 'EMAIL', - 'FAMC', - 'FAMS', - 'HUSB', - 'NAME', - 'NOTE', - 'OBJE', - 'PHON', - 'RESI', - 'RESN', - 'SEX', - 'SOUR', - 'SSN', - 'SUBM', - 'TITL', - 'URL', - 'WIFE', - 'WWW', - '_EMAIL', - '_TODO', - '_UID', - '_WT_OBJE_SORT', - ]; +class IndividualController extends AbstractBaseController +{ + // Do not show these facts in the expanded chart boxes. + const EXCLUDE_CHART_FACTS = [ + 'ADDR', + 'ALIA', + 'ASSO', + 'CHAN', + 'CHIL', + 'EMAIL', + 'FAMC', + 'FAMS', + 'HUSB', + 'NAME', + 'NOTE', + 'OBJE', + 'PHON', + 'RESI', + 'RESN', + 'SEX', + 'SOUR', + 'SSN', + 'SUBM', + 'TITL', + 'URL', + 'WIFE', + 'WWW', + '_EMAIL', + '_TODO', + '_UID', + '_WT_OBJE_SORT', + ]; - /** - * Show a individual's page. - * - * @param Request $request - * - * @return Response - */ - public function show(Request $request): Response { - /** @var Tree $tree */ - $tree = $request->attributes->get('tree'); - $xref = $request->get('xref'); - $individual = Individual::getInstance($xref, $tree); + /** + * Show a individual's page. + * + * @param Request $request + * + * @return Response + */ + public function show(Request $request): Response + { + /** @var Tree $tree */ + $tree = $request->attributes->get('tree'); + $xref = $request->get('xref'); + $individual = Individual::getInstance($xref, $tree); - $this->checkIndividualAccess($individual, false); + $this->checkIndividualAccess($individual, false); - // What is (was) the age of the individual - $bdate = $individual->getBirthDate(); - $ddate = $individual->getDeathDate(); - if ($bdate->isOK() && !$individual->isDead()) { - // If living display age - $age = ' (' . I18N::translate('age') . ' ' . FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($bdate, new Date(strtoupper(date('d M Y'))))) . ')'; - } elseif ($bdate->isOK() && $ddate->isOK()) { - // If dead, show age at death - $age = ' (' . I18N::translate('age') . ' ' . FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($bdate, $ddate)) . ')'; - } else { - $age = ''; - } + // What is (was) the age of the individual + $bdate = $individual->getBirthDate(); + $ddate = $individual->getDeathDate(); + if ($bdate->isOK() && !$individual->isDead()) { + // If living display age + $age = ' (' . I18N::translate('age') . ' ' . FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($bdate, new Date(strtoupper(date('d M Y'))))) . ')'; + } elseif ($bdate->isOK() && $ddate->isOK()) { + // If dead, show age at death + $age = ' (' . I18N::translate('age') . ' ' . FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($bdate, $ddate)) . ')'; + } else { + $age = ''; + } - // What images are linked to this individual - $individual_media = []; - foreach ($individual->getFacts() as $fact) { - $media_object = $fact->getTarget(); - if ($media_object instanceof Media) { - $individual_media[] = $media_object->firstImageFile(); - } - } - $individual_media = array_filter($individual_media); + // What images are linked to this individual + $individual_media = []; + foreach ($individual->getFacts() as $fact) { + $media_object = $fact->getTarget(); + if ($media_object instanceof Media) { + $individual_media[] = $media_object->firstImageFile(); + } + } + $individual_media = array_filter($individual_media); - $name_records = []; - foreach ($individual->getFacts('NAME') as $n => $name_fact) { - $name_records[] = $this->formatNameRecord($tree, $n, $name_fact); - } + $name_records = []; + foreach ($individual->getFacts('NAME') as $n => $name_fact) { + $name_records[] = $this->formatNameRecord($tree, $n, $name_fact); + } - $sex_records = []; - foreach ($individual->getFacts('SEX') as $n => $sex_fact) { - $sex_records[] = $this->formatSexRecord($sex_fact); - } + $sex_records = []; + foreach ($individual->getFacts('SEX') as $n => $sex_fact) { + $sex_records[] = $this->formatSexRecord($sex_fact); + } - // If this individual is linked to a user account, show the link - $user_link = ''; - if (Auth::isAdmin()) { - $user = User::findByIndividual($individual); - if ($user) { - $user_link = ' — <a href="' . e(route('admin-users', ['filter' => $user->getEmail()])) . '">' . e($user->getUserName()) . '</a>'; - }; - } + // If this individual is linked to a user account, show the link + $user_link = ''; + if (Auth::isAdmin()) { + $user = User::findByIndividual($individual); + if ($user) { + $user_link = ' — <a href="' . e(route('admin-users', ['filter' => $user->getEmail()])) . '">' . e($user->getUserName()) . '</a>'; + }; + } - return $this->viewResponse('individual-page', [ - 'age' => $age, - 'count_media' => $this->countFacts($individual, 'OBJE'), - 'count_names' => $this->countFacts($individual, 'NAME'), - 'count_sex' => $this->countFacts($individual, 'SEX'), - 'individual' => $individual, - 'individual_media' => $individual_media, - 'meta_robots' => 'index,follow', - 'name_records' => $name_records, - 'sex_records' => $sex_records, - 'sidebars' => $this->getSidebars($individual), - 'tabs' => $this->getTabs($individual), - 'significant' => $this->significant($individual), - 'title' => $individual->getFullName() . ' ' . $individual->getLifeSpan(), - 'user_link' => $user_link, - ]); - } + return $this->viewResponse('individual-page', [ + 'age' => $age, + 'count_media' => $this->countFacts($individual, 'OBJE'), + 'count_names' => $this->countFacts($individual, 'NAME'), + 'count_sex' => $this->countFacts($individual, 'SEX'), + 'individual' => $individual, + 'individual_media' => $individual_media, + 'meta_robots' => 'index,follow', + 'name_records' => $name_records, + 'sex_records' => $sex_records, + 'sidebars' => $this->getSidebars($individual), + 'tabs' => $this->getTabs($individual), + 'significant' => $this->significant($individual), + 'title' => $individual->getFullName() . ' ' . $individual->getLifeSpan(), + 'user_link' => $user_link, + ]); + } - /** - * @param Request $request - * - * @return Response - */ - public function tab(Request $request): Response { - /** @var Tree $tree */ - $tree = $request->attributes->get('tree'); - $xref = $request->get('xref'); - $record = Individual::getInstance($xref, $tree); - $tab = $request->get('module'); - $tabs = Module::getActiveTabs($tree); + /** + * @param Request $request + * + * @return Response + */ + public function tab(Request $request): Response + { + /** @var Tree $tree */ + $tree = $request->attributes->get('tree'); + $xref = $request->get('xref'); + $record = Individual::getInstance($xref, $tree); + $tab = $request->get('module'); + $tabs = Module::getActiveTabs($tree); - if ($record === null || !array_key_exists($tab, $tabs)) { - return new Response('', Response::HTTP_NOT_FOUND); - } elseif (!$record->canShow()) { - return new Response('', Response::HTTP_FORBIDDEN); - } else { - $tab = $tabs[$tab]; + if ($record === null || !array_key_exists($tab, $tabs)) { + return new Response('', Response::HTTP_NOT_FOUND); + } elseif (!$record->canShow()) { + return new Response('', Response::HTTP_FORBIDDEN); + } else { + $tab = $tabs[$tab]; - $layout = view('layouts/ajax', [ - 'content' => $tab->getTabContent($record), - ]); + $layout = view('layouts/ajax', [ + 'content' => $tab->getTabContent($record), + ]); - return new Response($layout); - } - } + return new Response($layout); + } + } - /** - * Show additional details for a chart box. - * - * @param Request $request - * - * @return Response - */ - public function expandChartBox(Request $request): Response { - /** @var Tree $tree */ - $tree = $request->attributes->get('tree'); - $xref = $request->get('xref'); - $individual = Individual::getInstance($xref, $tree); + /** + * Show additional details for a chart box. + * + * @param Request $request + * + * @return Response + */ + public function expandChartBox(Request $request): Response + { + /** @var Tree $tree */ + $tree = $request->attributes->get('tree'); + $xref = $request->get('xref'); + $individual = Individual::getInstance($xref, $tree); - $this->checkIndividualAccess($individual, false); + $this->checkIndividualAccess($individual, false); - $facts = $individual->getFacts(); - foreach ($individual->getSpouseFamilies() as $family) { - foreach ($family->getFacts() as $fact) { - $facts[] = $fact; - } - } - Functions::sortFacts($facts); + $facts = $individual->getFacts(); + foreach ($individual->getSpouseFamilies() as $family) { + foreach ($family->getFacts() as $fact) { + $facts[] = $fact; + } + } + Functions::sortFacts($facts); - $facts = array_filter($facts, function (Fact $fact) { - return !in_array($fact->getTag(), self::EXCLUDE_CHART_FACTS); - }); + $facts = array_filter($facts, function (Fact $fact) { + return !in_array($fact->getTag(), self::EXCLUDE_CHART_FACTS); + }); - $html = view('expand-chart-box', [ - 'facts' => $facts, - ]); + $html = view('expand-chart-box', [ + 'facts' => $facts, + ]); - return new Response($html); - } + return new Response($html); + } - /** - * Count the (non-pending-delete) name records for an individual. - * - * @param Individual $individual - * @param string $fact_name - * - * @return int - */ - private function countFacts(Individual $individual, $fact_name): int { - $count = 0; + /** + * Count the (non-pending-delete) name records for an individual. + * + * @param Individual $individual + * @param string $fact_name + * + * @return int + */ + private function countFacts(Individual $individual, $fact_name): int + { + $count = 0; - foreach ($individual->getFacts($fact_name) as $fact) { - if (!$fact->isPendingDeletion()) { - $count++; - } - } + foreach ($individual->getFacts($fact_name) as $fact) { + if (!$fact->isPendingDeletion()) { + $count++; + } + } - return $count; - } + return $count; + } - /** - * Format a name record - * - * @param Tree $tree - * @param int $n - * @param Fact $fact - * - * @return string - */ - private function formatNameRecord(Tree $tree, $n, Fact $fact) { - $individual = $fact->getParent(); + /** + * Format a name record + * + * @param Tree $tree + * @param int $n + * @param Fact $fact + * + * @return string + */ + private function formatNameRecord(Tree $tree, $n, Fact $fact) + { + $individual = $fact->getParent(); - // Create a dummy record, so we can extract the formatted NAME value from it. - $dummy = new Individual( - 'xref', - "0 @xref@ INDI\n1 DEAT Y\n" . $fact->getGedcom(), - null, - $individual->getTree() - ); - $dummy->setPrimaryName(0); // Make sure we use the name from "1 NAME" + // Create a dummy record, so we can extract the formatted NAME value from it. + $dummy = new Individual( + 'xref', + "0 @xref@ INDI\n1 DEAT Y\n" . $fact->getGedcom(), + null, + $individual->getTree() + ); + $dummy->setPrimaryName(0); // Make sure we use the name from "1 NAME" - $container_class = 'card'; - $content_class = 'collapse'; - $aria = 'false'; + $container_class = 'card'; + $content_class = 'collapse'; + $aria = 'false'; - if ($n === 0) { - $content_class = 'collapse show'; - $aria = 'true'; - } - if ($fact->isPendingDeletion()) { - $container_class .= ' old'; - } elseif ($fact->isPendingAddition()) { - $container_class .= ' new'; - } + if ($n === 0) { + $content_class = 'collapse show'; + $aria = 'true'; + } + if ($fact->isPendingDeletion()) { + $container_class .= ' old'; + } elseif ($fact->isPendingAddition()) { + $container_class .= ' new'; + } - ob_start(); - echo '<dl><dt class="label">', I18N::translate('Name'), '</dt>'; - echo '<dd class="field">', $dummy->getFullName(), '</dd>'; - $ct = preg_match_all('/\n2 (\w+) (.*)/', $fact->getGedcom(), $nmatch, PREG_SET_ORDER); - for ($i = 0; $i < $ct; $i++) { - $tag = $nmatch[$i][1]; - if ($tag != 'SOUR' && $tag != 'NOTE' && $tag != 'SPFX') { - echo '<dt class="label">', GedcomTag::getLabel($tag, $individual), '</dt>'; - echo '<dd class="field">'; // Before using dir="auto" on this field, note that Gecko treats this as an inline element but WebKit treats it as a block element - if (isset($nmatch[$i][2])) { - $name = e($nmatch[$i][2]); - $name = str_replace('/', '', $name); - $name = preg_replace('/(\S*)\*/', '<span class="starredname">\\1</span>', $name); - switch ($tag) { - case 'TYPE': - echo GedcomCodeName::getValue($name, $individual); - break; - case 'SURN': - // The SURN field is not necessarily the surname. - // Where it is not a substring of the real surname, show it after the real surname. - $surname = e($dummy->getAllNames()[0]['surname']); - $surns = preg_replace('/, */', ' ', $nmatch[$i][2]); - if (strpos($dummy->getAllNames()[0]['surname'], $surns) !== false) { - echo '<span dir="auto">' . $surname . '</span>'; - } else { - echo I18N::translate('%1$s (%2$s)', '<span dir="auto">' . $surname . '</span>', '<span dir="auto">' . $name . '</span>'); - } - break; - default: - echo '<span dir="auto">' . $name . '</span>'; - break; - } - } - echo '</dd>'; - echo '</dl>'; - } - } - if (preg_match("/\n2 SOUR/", $fact->getGedcom())) { - echo '<div id="indi_sour" class="clearfloat">', FunctionsPrintFacts::printFactSources($tree, $fact->getGedcom(), 2), '</div>'; - } - if (preg_match("/\n2 NOTE/", $fact->getGedcom())) { - echo '<div id="indi_note" class="clearfloat">', FunctionsPrint::printFactNotes($tree, $fact->getGedcom(), 2), '</div>'; - } - $content = ob_get_clean(); + ob_start(); + echo '<dl><dt class="label">', I18N::translate('Name'), '</dt>'; + echo '<dd class="field">', $dummy->getFullName(), '</dd>'; + $ct = preg_match_all('/\n2 (\w+) (.*)/', $fact->getGedcom(), $nmatch, PREG_SET_ORDER); + for ($i = 0; $i < $ct; $i++) { + $tag = $nmatch[$i][1]; + if ($tag != 'SOUR' && $tag != 'NOTE' && $tag != 'SPFX') { + echo '<dt class="label">', GedcomTag::getLabel($tag, $individual), '</dt>'; + echo '<dd class="field">'; // Before using dir="auto" on this field, note that Gecko treats this as an inline element but WebKit treats it as a block element + if (isset($nmatch[$i][2])) { + $name = e($nmatch[$i][2]); + $name = str_replace('/', '', $name); + $name = preg_replace('/(\S*)\*/', '<span class="starredname">\\1</span>', $name); + switch ($tag) { + case 'TYPE': + echo GedcomCodeName::getValue($name, $individual); + break; + case 'SURN': + // The SURN field is not necessarily the surname. + // Where it is not a substring of the real surname, show it after the real surname. + $surname = e($dummy->getAllNames()[0]['surname']); + $surns = preg_replace('/, */', ' ', $nmatch[$i][2]); + if (strpos($dummy->getAllNames()[0]['surname'], $surns) !== false) { + echo '<span dir="auto">' . $surname . '</span>'; + } else { + echo I18N::translate('%1$s (%2$s)', '<span dir="auto">' . $surname . '</span>', '<span dir="auto">' . $name . '</span>'); + } + break; + default: + echo '<span dir="auto">' . $name . '</span>'; + break; + } + } + echo '</dd>'; + echo '</dl>'; + } + } + if (preg_match("/\n2 SOUR/", $fact->getGedcom())) { + echo '<div id="indi_sour" class="clearfloat">', FunctionsPrintFacts::printFactSources($tree, $fact->getGedcom(), 2), '</div>'; + } + if (preg_match("/\n2 NOTE/", $fact->getGedcom())) { + echo '<div id="indi_note" class="clearfloat">', FunctionsPrint::printFactNotes($tree, $fact->getGedcom(), 2), '</div>'; + } + $content = ob_get_clean(); - if ($individual->canEdit() && !$fact->isPendingDeletion()) { - $edit_links = - FontAwesome::linkIcon('delete', I18N::translate('Delete this name'), [ - 'class' => 'btn btn-link', - 'href' => '#', - 'onclick' => 'return delete_fact("' . I18N::translate('Are you sure you want to delete this fact?') . '", "' . e($individual->getTree()->getName()) . '", "' . e($individual->getXref()) . '", "' . $fact->getFactId() . '");', - ]) . - FontAwesome::linkIcon('edit', I18N::translate('Edit the name'), [ - 'class' => 'btn btn-link', - 'href' => route('edit-name', ['xref' => $individual->getXref(), 'fact_id' => $fact->getFactId(), 'ged' => $individual->getTree()->getName()]), - ]); - } else { - $edit_links = ''; - } + if ($individual->canEdit() && !$fact->isPendingDeletion()) { + $edit_links = + FontAwesome::linkIcon('delete', I18N::translate('Delete this name'), [ + 'class' => 'btn btn-link', + 'href' => '#', + 'onclick' => 'return delete_fact("' . I18N::translate('Are you sure you want to delete this fact?') . '", "' . e($individual->getTree()->getName()) . '", "' . e($individual->getXref()) . '", "' . $fact->getFactId() . '");', + ]) . + FontAwesome::linkIcon('edit', I18N::translate('Edit the name'), [ + 'class' => 'btn btn-link', + 'href' => route('edit-name', ['xref' => $individual->getXref(), + 'fact_id' => $fact->getFactId(), + 'ged' => $individual->getTree()->getName(), + ]), + ]); + } else { + $edit_links = ''; + } - return ' + return ' <div class="' . $container_class . '"> <div class="card-header" role="tab" id="name-header-' . $n . '"> <a data-toggle="collapse" data-parent="#individual-names" href="#name-content-' . $n . '" aria-expanded="' . $aria . '" aria-controls="name-content-' . $n . '">' . $dummy->getFullName() . '</a> @@ -336,47 +345,51 @@ class IndividualController extends AbstractBaseController { <div class="card-body">' . $content . '</div> </div> </div>'; - } + } - /** - * print information for a sex record - * - * @param Fact $fact - * - * @return string - */ - private function formatSexRecord(Fact $fact) { - $individual = $fact->getParent(); + /** + * print information for a sex record + * + * @param Fact $fact + * + * @return string + */ + private function formatSexRecord(Fact $fact) + { + $individual = $fact->getParent(); - switch ($fact->getValue()) { - case 'M': - $sex = I18N::translate('Male'); - break; - case 'F': - $sex = I18N::translate('Female'); - break; - default: - $sex = I18N::translateContext('unknown gender', 'Unknown'); - break; - } + switch ($fact->getValue()) { + case 'M': + $sex = I18N::translate('Male'); + break; + case 'F': + $sex = I18N::translate('Female'); + break; + default: + $sex = I18N::translateContext('unknown gender', 'Unknown'); + break; + } - $container_class = 'card'; - if ($fact->isPendingDeletion()) { - $container_class .= ' old'; - } elseif ($fact->isPendingAddition()) { - $container_class .= ' new'; - } + $container_class = 'card'; + if ($fact->isPendingDeletion()) { + $container_class .= ' old'; + } elseif ($fact->isPendingAddition()) { + $container_class .= ' new'; + } - if ($individual->canEdit() && !$fact->isPendingDeletion()) { - $edit_links = FontAwesome::linkIcon('edit', I18N::translate('Edit the gender'), [ - 'class' => 'btn btn-link', - 'href' => route('edit-fact', ['xref' => $individual->getXref(), 'fact_id' => $fact->getFactId(), 'ged' => $individual->getTree()->getName()]) - ]); - } else { - $edit_links = ''; - } + if ($individual->canEdit() && !$fact->isPendingDeletion()) { + $edit_links = FontAwesome::linkIcon('edit', I18N::translate('Edit the gender'), [ + 'class' => 'btn btn-link', + 'href' => route('edit-fact', ['xref' => $individual->getXref(), + 'fact_id' => $fact->getFactId(), + 'ged' => $individual->getTree()->getName(), + ]), + ]); + } else { + $edit_links = ''; + } - return ' + return ' <div class="' . $container_class . '"> <div class="card-header" role="tab" id="name-header-add"> <div class="card-title mb-0"> @@ -384,62 +397,65 @@ class IndividualController extends AbstractBaseController { </div> </div> </div>'; - } + } - /** - * Which tabs should we show on this individual's page. - * We don't show empty tabs. - * - * @param Individual $individual - * - * @return ModuleTabInterface[] - */ - public function getSidebars(Individual $individual) { - $sidebars = Module::getActiveSidebars($individual->getTree()); + /** + * Which tabs should we show on this individual's page. + * We don't show empty tabs. + * + * @param Individual $individual + * + * @return ModuleTabInterface[] + */ + public function getSidebars(Individual $individual) + { + $sidebars = Module::getActiveSidebars($individual->getTree()); - return array_filter($sidebars, function (ModuleSidebarInterface $sidebar) use ($individual) { - return $sidebar->hasSidebarContent($individual); - }); - } + return array_filter($sidebars, function (ModuleSidebarInterface $sidebar) use ($individual) { + return $sidebar->hasSidebarContent($individual); + }); + } - /** - * Which tabs should we show on this individual's page. - * We don't show empty tabs. - * - * @param Individual $individual - * - * @return ModuleTabInterface[] - */ - public function getTabs(Individual $individual) { - $tabs = Module::getActiveTabs($individual->getTree()); + /** + * Which tabs should we show on this individual's page. + * We don't show empty tabs. + * + * @param Individual $individual + * + * @return ModuleTabInterface[] + */ + public function getTabs(Individual $individual) + { + $tabs = Module::getActiveTabs($individual->getTree()); - return array_filter($tabs, function (ModuleTabInterface $tab) use ($individual) { - return $tab->hasTabContent($individual); - }); - } + return array_filter($tabs, function (ModuleTabInterface $tab) use ($individual) { + return $tab->hasTabContent($individual); + }); + } - /** - * What are the significant elements of this page? - * The layout will need them to generate URLs for charts and reports. - * - * @param Individual $individual - * - * @return stdClass - */ - private function significant(Individual $individual) { - $significant = (object) [ - 'family' => null, - 'individual' => $individual, - 'surname' => '', - ]; + /** + * What are the significant elements of this page? + * The layout will need them to generate URLs for charts and reports. + * + * @param Individual $individual + * + * @return stdClass + */ + private function significant(Individual $individual) + { + $significant = (object)[ + 'family' => null, + 'individual' => $individual, + 'surname' => '', + ]; - list($significant->surname) = explode(',', $individual->getSortName()); + list($significant->surname) = explode(',', $individual->getSortName()); - foreach ($individual->getChildFamilies() + $individual->getSpouseFamilies() as $family) { - $significant->family = $family; - break; - } + foreach ($individual->getChildFamilies() + $individual->getSpouseFamilies() as $family) { + $significant->family = $family; + break; + } - return $significant; - } + return $significant; + } } |
