. */ namespace Fisharebest\Webtrees\Controller; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Database; use Fisharebest\Webtrees\Fact; use Fisharebest\Webtrees\Family; use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\Functions\FunctionsPrint; use Fisharebest\Webtrees\Functions\FunctionsPrintFacts; use Fisharebest\Webtrees\GedcomCode\GedcomCodeName; use Fisharebest\Webtrees\GedcomTag; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Menu; use Fisharebest\Webtrees\Module; use Fisharebest\Webtrees\User; /** * Controller for the individual page */ class IndividualController extends GedcomRecordController { /** @var int Count of names */ public $name_count = 0; /** @var int Count of names. */ public $total_names = 0; /** ModuleTabInterface[] List of tabs to show */ public $tabs; /** * Startup activity * * @param Individual|null $record */ public function __construct($record) { parent::__construct($record); // If we can display the details, add them to the page header if ($this->record && $this->record->canShow()) { $this->setPageTitle($this->record->getFullName() . ' ' . $this->record->getLifeSpan()); $this->tabs = Module::getActiveTabs($this->record->getTree()); } } /** * Get significant information from this page, to allow other pages such as * charts and reports to initialise with the same records * * @return Individual */ public function getSignificantIndividual() { if ($this->record) { return $this->record; } return parent::getSignificantIndividual(); } /** * Get significant information from this page, to allow other pages such as * charts and reports to initialise with the same records * * @return Family */ public function getSignificantFamily() { if ($this->record) { foreach ($this->record->getChildFamilies() as $family) { return $family; } foreach ($this->record->getSpouseFamilies() as $family) { return $family; } } return parent::getSignificantFamily(); } /** * Handle AJAX requests - to generate the tab content */ public function ajaxRequest() { // Search engines should not make AJAX requests if (Auth::isSearchEngine()) { http_response_code(403); exit; } // Initialise tabs $tab = Filter::get('module'); // A request for a non-existant tab? if (array_key_exists($tab, $this->tabs)) { $mod = $this->tabs[$tab]; } else { http_response_code(404); exit; } header("Content-Type: text/html; charset=UTF-8"); // AJAX calls do not have the meta tag headers and need this set header("X-Robots-Tag: noindex,follow"); // AJAX pages should not show up in search results, any links can be followed though echo $mod->getTabContent(); if (WT_DEBUG_SQL) { echo Database::getQueryLog(); } } /** * print information for a name record * * @param Fact $event the event object */ public function printNameRecord(Fact $event) { $factrec = $event->getGedcom(); // Create a dummy record, so we can extract the formatted NAME value from the event. $dummy = new Individual( 'xref', "0 @xref@ INDI\n1 DEAT Y\n" . $factrec, null, $event->getParent()->getTree() ); $all_names = $dummy->getAllNames(); $primary_name = $all_names[0]; $this->name_count++; if ($this->name_count > 1) { echo '

', $dummy->getFullName(), '

'; } // Other names accordion element echo '
'; echo '
'; echo '
', I18N::translate('Name'), '
'; $dummy->setPrimaryName(0); echo '
', $dummy->getFullName(); if ($this->name_count == 1) { if (Auth::isAdmin()) { $user = User::findByGenealogyRecord($this->record); if ($user) { echo ' - ' . Filter::escapeHtml($user->getUserName()) . ''; } } } if ($this->record->canEdit() && !$event->isPendingDeletion()) { echo ""; echo ""; } echo '
'; echo '
'; echo '
'; $ct = preg_match_all('/\n2 (\w+) (.*)/', $factrec, $nmatch, PREG_SET_ORDER); for ($i = 0; $i < $ct; $i++) { echo '
'; $fact = $nmatch[$i][1]; if ($fact != 'SOUR' && $fact != 'NOTE' && $fact != 'SPFX') { echo '
', GedcomTag::getLabel($fact, $this->record), '
'; echo '
'; // 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 = Filter::escapeHtml($nmatch[$i][2]); $name = str_replace('/', '', $name); $name = preg_replace('/(\S*)\*/', '\\1', $name); switch ($fact) { case 'TYPE': echo GedcomCodeName::getValue($name, $this->record); 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 = Filter::escapeHtml($primary_name['surname']); if (strpos($primary_name['surname'], str_replace(',', ' ', $nmatch[$i][2])) !== false) { echo '' . $surname . ''; } else { echo I18N::translate('%1$s (%2$s)', '' . $surname . '', '' . $name . ''); } break; default: echo '' . $name . ''; break; } } echo '
'; echo '
'; } echo '
'; } if (preg_match("/\n2 SOUR/", $factrec)) { echo '
', FunctionsPrintFacts::printFactSources($factrec, 2), '
'; } if (preg_match("/\n2 NOTE/", $factrec)) { echo '
', FunctionsPrint::printFactNotes($factrec, 2), '
'; } echo '
'; } /** * print information for a sex record * * @param Fact $event the Event object */ public function printSexRecord(Fact $event) { $sex = $event->getValue(); if (empty($sex)) { $sex = 'U'; } echo 'canEdit()) { echo ' title="', I18N::translate('Male'), ' - ', I18N::translate('Edit'), '"'; echo ' onclick="edit_record(\'' . $this->record->getXref() . '\', \'' . $event->getFactId() . '\'); return false;">'; } else { echo ' title="', I18N::translate('Male'), '">'; } break; case 'F': echo 'female_gender"'; if ($event->canEdit()) { echo ' title="', I18N::translate('Female'), ' - ', I18N::translate('Edit'), '"'; echo ' onclick="edit_record(\'' . $this->record->getXref() . '\', \'' . $event->getFactId() . '\'); return false;">'; } else { echo ' title="', I18N::translate('Female'), '">'; } break; default: echo 'unknown_gender"'; if ($event->canEdit()) { echo ' title="', I18N::translateContext('unknown gender', 'Unknown'), ' - ', I18N::translate('Edit'), '"'; echo ' onclick="edit_record(\'' . $this->record->getXref() . '\', \'' . $event->getFactId() . '\'); return false;">'; } else { echo ' title="', I18N::translateContext('unknown gender', 'Unknown'), '">'; } break; } echo ''; } /** * get edit menu */ public function getEditMenu() { if (!$this->record || $this->record->isPendingDeletion()) { return null; } // edit menu $menu = new Menu(I18N::translate('Edit'), '#', 'menu-indi'); if (Auth::isEditor($this->record->getTree())) { $menu->addSubmenu(new Menu(I18N::translate('Add a name'), '#', 'menu-indi-addname', array( 'onclick' => 'return add_name("' . $this->record->getXref() . '");', ))); $has_sex_record = false; foreach ($this->record->getFacts() as $fact) { if ($fact->getTag() === 'SEX' && $fact->canEdit()) { $menu->addSubmenu(new Menu(I18N::translate('Edit the gender'), '#', 'menu-indi-editsex', array( 'onclick' => 'return edit_record("' . $this->record->getXref() . '", "' . $fact->getFactId() . '");', ))); $has_sex_record = true; break; } } if (!$has_sex_record) { $menu->addSubmenu(new Menu(I18N::translate('Edit the gender'), '#', 'menu-indi-editsex', array( 'onclick' => 'return add_new_record("' . $this->record->getXref() . '", "SEX");', ))); } if (count($this->record->getSpouseFamilies()) > 1) { $menu->addSubmenu(new Menu(I18N::translate('Re-order families'), '#', 'menu-indi-orderfam', array( 'onclick' => 'return reorder_families("' . $this->record->getXref() . '");', ))); } // delete $menu->addSubmenu(new Menu(I18N::translate('Delete'), '#', 'menu-indi-del', array( 'onclick' => 'return delete_record("' . I18N::translate('Are you sure you want to delete ā€œ%sā€?', Filter::escapeJs(Filter::unescapeHtml($this->record->getFullName()))) . '", "' . $this->record->getXref() . '");', ))); } // edit raw if (Auth::isAdmin() || Auth::isEditor($this->record->getTree()) && $this->record->getTree()->getPreference('SHOW_GEDCOM_RECORD')) { $menu->addSubmenu(new Menu(I18N::translate('Edit the raw GEDCOM'), '#', 'menu-indi-editraw', array( 'onclick' => 'return edit_raw("' . $this->record->getXref() . '");', ))); } return $menu; } /** * get the person box stylesheet class for the given person * * @param Individual $person * * @return string returns 'person_box', 'person_boxF', or 'person_boxNN' */ public function getPersonStyle($person) { switch ($person->getSex()) { case 'M': $class = 'person_box'; break; case 'F': $class = 'person_boxF'; break; default: $class = 'person_boxNN'; break; } if ($person->isPendingDeletion()) { $class .= ' old'; } elseif ($person->isPendingAddtion()) { $class .= ' new'; } return $class; } /** * Get significant information from this page, to allow other pages such as * charts and reports to initialise with the same records * * @return string */ public function getSignificantSurname() { if ($this->record) { list($surn) = explode(',', $this->record->getSortName()); return $surn; } else { return ''; } } /** * Get the contents of sidebar. * * @return string */ public function getSideBarContent() { global $controller; $html = ''; $active = 0; $n = 0; foreach (Module::getActiveSidebars($this->record->getTree()) as $mod) { if ($mod->hasSidebarContent()) { $html .= '

' . $mod->getTitle() . '

'; $html .= '
' . $mod->getSidebarContent() . '
'; // The family navigator should be opened by default if ($mod->getName() == 'family_nav') { $active = $n; } ++$n; } } if ($html) { $controller ->addInlineJavascript(' jQuery("#sidebarAccordion").accordion({ active:' . $active . ', heightStyle: "content", collapsible: true, }); '); return ''; } else { return ''; } } /** * Get the description for the family. * * For example, "XXX's family with new wife". * * @param Family $family * @param Individual $individual * * @return string */ public function getSpouseFamilyLabel(Family $family, Individual $individual) { $spouse = $family->getSpouse($individual); if ($spouse) { return /* I18N: %s is the spouse name */ I18N::translate('Family with %s', $spouse->getFullName()); } else { return $family->getFullName(); } } }