/** * Calculate the shortest paths - or all paths - between two individuals. * * @param Individual $individual1 * @param Individual $individual2 * @param int $recursion How many levels of recursion to use * @param boo; $ancestor Restrict to relationships via a common ancestor * * @return string[][] */ public function calculateRelationships(Individual $individual1, Individual $individual2, $recursion, $ancestor = false) { $rows = Database::prepare("SELECT l_from, l_to FROM `##link` WHERE l_file = :tree_id AND l_type IN ('FAMS', 'FAMC')")->execute(array('tree_id' => $individual1->getTree()->getTreeId()))->fetchAll(); // Optionally restrict the graph to the ancestors of the individuals. if ($ancestor) { $ancestors = $this->allAncestors($individual1->getXref(), $individual2->getXref(), $individual1->getTree()->getTreeId()); $exclude = $this->excludeFamilies($individual1->getXref(), $individual2->getXref(), $individual1->getTree()->getTreeId()); } else { $ancestors = array(); $exclude = array(); } $graph = array(); foreach ($rows as $row) { if (!$ancestors || in_array($row->l_from, $ancestors) && !in_array($row->l_to, $exclude)) { $graph[$row->l_from][$row->l_to] = 1; $graph[$row->l_to][$row->l_from] = 1; } } $xref1 = $individual1->getXref(); $xref2 = $individual2->getXref(); $dijkstra = new Dijkstra($graph); $paths = $dijkstra->shortestPaths($xref1, $xref2); // Only process each exclusion list once; $excluded = array(); $queue = array(); foreach ($paths as $path) { // Insert the paths into the queue, with an exclusion list. $queue[] = array('path' => $path, 'exclude' => array()); // While there are un-extended paths while (list(, $next) = each($queue)) { // For each family on the path for ($n = count($next['path']) - 2; $n >= 1; $n -= 2) { $exclude = $next['exclude']; if (count($exclude) >= $recursion) { continue; } $exclude[] = $next['path'][$n]; sort($exclude); $tmp = implode('-', $exclude); if (in_array($tmp, $excluded)) { continue; } else { $excluded[] = $tmp; } // Add any new path to the queue foreach ($dijkstra->shortestPaths($xref1, $xref2, $exclude) as $new_path) { $queue[] = array('path' => $new_path, 'exclude' => $exclude); } } } } // Extract the paths from the queue, removing duplicates. $paths = array(); foreach ($queue as $next) { $paths[implode('-', $next['path'])] = $next['path']; } return $paths; }
/** * get edit menu */ public function getEditMenu() { if (!$this->record || $this->record->isPendingDeletion()) { return null; } // edit menu $menu = new Menu(I18N::translate('Edit'), '#', 'menu-record'); // edit raw if (Auth::isAdmin() || Auth::isEditor($this->record->getTree()) && $this->record->getTree()->getPreference('SHOW_GEDCOM_RECORD')) { $menu->addSubmenu(new Menu(I18N::translate('Edit raw GEDCOM'), '#', 'menu-record-editraw', array('onclick' => 'return edit_raw("' . $this->record->getXref() . '");'))); } // delete if (Auth::isEditor($this->record->getTree())) { $menu->addSubmenu(new Menu(I18N::translate('Delete'), '#', 'menu-record-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() . '");'))); } // add to favorites if (Module::getModuleByName('user_favorites')) { $menu->addSubmenu(new Menu(I18N::translate('Add to favorites'), '#', 'menu-record-addfav', array('onclick' => 'jQuery.post("module.php?mod=user_favorites&mod_action=menu-add-favorite" ,{xref:"' . $this->record->getXref() . '"},function(){location.reload();})'))); } // Get the link for the first submenu and set it as the link for the main menu if ($menu->getSubmenus()) { $submenus = $menu->getSubmenus(); $menu->setLink($submenus[0]->getLink()); $menu->setAttrs($submenus[0]->getAttrs()); } return $menu; }
/** * Return a menu item for this chart. * * We can only do this if the GD2 library is installed with TrueType support. * * @return Menu|null */ public function getChartMenu(Individual $individual) { if (function_exists('imagettftext')) { return new Menu($this->getTitle(), 'fanchart.php?rootid=' . $individual->getXref() . '&ged=' . $individual->getTree()->getNameUrl(), 'menu-chart-fanchart', array('rel' => 'nofollow')); } else { return null; } }
/** * Startup activity */ public function __construct() { // Automatically fix broken links if ($this->record && $this->record->canEdit()) { $broken_links = 0; foreach ($this->record->getFacts('HUSB|WIFE|CHIL|FAMS|FAMC|REPO') as $fact) { if (!$fact->isPendingDeletion() && $fact->getTarget() === null) { $this->record->deleteFact($fact->getFactId(), false); FlashMessages::addMessage(I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $this->record->getFullName(), $fact->getValue())); $broken_links = true; } } foreach ($this->record->getFacts('NOTE|SOUR|OBJE') as $fact) { // These can be links or inline. Only delete links. if (!$fact->isPendingDeletion() && $fact->getTarget() === null && preg_match('/^@.*@$/', $fact->getValue())) { $this->record->deleteFact($fact->getFactId(), false); FlashMessages::addMessage(I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $this->record->getFullName(), $fact->getValue())); $broken_links = true; } } if ($broken_links) { // Reload the updated family $this->record = GedcomRecord::getInstance($this->record->getXref(), $this->record->getTree()); } } parent::__construct(); // We want robots to index this page $this->setMetaRobots('index,follow'); // Set a page title if ($this->record) { if ($this->record->canShowName()) { // e.g. "John Doe" or "1881 Census of Wales" $this->setPageTitle($this->record->getFullName()); } else { // e.g. "Individual" or "Source" $record = $this->record; $this->setPageTitle(GedcomTag::getLabel($record::RECORD_TYPE)); } } else { // No such record $this->setPageTitle(I18N::translate('Private')); } }
/** * Return a menu item for this chart. * * @param Individual $individual * * @return Menu|null */ public function getChartMenu(Individual $individual) { $tree = $individual->getTree(); $gedcomid = $tree->getUserPreference(Auth::user(), 'gedcomid'); if ($gedcomid) { return new Menu(I18N::translate('Relationship to me'), 'relationship.php?pid1=' . $gedcomid . '&pid2=' . $individual->getXref() . '&ged=' . $tree->getNameUrl(), 'menu-chart-relationship', array('rel' => 'nofollow')); } else { return new Menu(I18N::translate('Relationships'), 'relationship.php?pid1=' . $individual->getXref() . '&ged=' . $tree->getNameUrl(), 'menu-chart-relationship', array('rel' => 'nofollow')); } }
/** * get edit menu */ public function getEditMenu() { if (!$this->record || $this->record->isPendingDeletion()) { return null; } // edit menu $menu = new Menu(I18N::translate('Edit'), '#', 'menu-record'); // 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-record-editraw', array('onclick' => 'return edit_raw("' . $this->record->getXref() . '");'))); } // delete if (Auth::isEditor($this->record->getTree())) { $menu->addSubmenu(new Menu(I18N::translate('Delete'), '#', 'menu-record-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() . '");'))); } return $menu; }
/** * Does an individual (or their spouse-families) have any facts with places? * * @param Individual $individual * * @return bool */ private function checkMapData(Individual $individual) { $statement = Database::prepare("SELECT COUNT(*) FROM `##placelinks` WHERE pl_gid = :xref AND pl_file = :tree_id"); $args = array('xref' => $individual->getXref(), 'tree_id' => $individual->getTree()->getTreeId()); if ($statement->execute($args)->fetchOne()) { return true; } foreach ($individual->getSpouseFamilies() as $family) { $args['xref'] = $family->getXref(); if ($statement->execute($args)->fetchOne()) { return true; } } return false; }
/** * Return a menu item for this chart. * * @return Menu|null */ public function getChartMenu(Individual $individual) { return new Menu($this->getTitle(), 'compact.php?rootid=' . $individual->getXref() . '&ged=' . $individual->getTree()->getNameUrl(), 'menu-chart-compact', array('rel' => 'nofollow')); }
/** * Get any historical events. * * @param Individual $person * * @return Fact[] */ private static function historicalFacts(Individual $person) { $SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS'); $facts = array(); if ($SHOW_RELATIVES_EVENTS) { // Only include events between birth and death $birt_date = $person->getEstimatedBirthDate(); $deat_date = $person->getEstimatedDeathDate(); if (file_exists(Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) { $histo = array(); require Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php'; foreach ($histo as $hist) { // Earlier versions of the WIKI encouraged people to use HTML entities, // rather than UTF8 encoding. $hist = html_entity_decode($hist, ENT_QUOTES, 'UTF-8'); $fact = new Fact($hist, $person, 'histo'); $sdate = $fact->getDate(); if ($sdate->isOK() && Date::compare($birt_date, $sdate) <= 0 && Date::compare($sdate, $deat_date) <= 0) { $facts[] = $fact; } } } } return $facts; }
/** * Create part of an individual box * * @param Individual $individual * * @return string */ protected function individualBoxSexSymbol(Individual $individual) { if ($individual->getTree()->getPreference('PEDIGREE_SHOW_GENDER')) { return $individual->sexImage('large'); } else { return ''; } }
/** * Get the thumbnail image for the given person * * @param Individual $individual * * @return string */ private function getThumbnail(Individual $individual) { if ($individual->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) { return $individual->displayImage(); } else { return ''; } }
/** * Return a menu item for this chart. * * @return Menu|null */ public function getChartMenu(Individual $individual) { return new Menu($this->getTitle(), 'statistics.php?ged=' . $individual->getTree()->getNameUrl(), 'menu-chart-statistics', array('rel' => 'nofollow')); }
/** * Return a menu item for this chart. * * @return Menu|null */ public function getChartMenu(Individual $individual) { return new Menu($this->getTitle(), 'module.php?mod=tree&mod_action=treeview&rootid=' . $individual->getXref() . '&ged=' . $individual->getTree()->getNameUrl(), 'menu-chart-tree', array('rel' => 'nofollow')); }
/** * Return a menu item for this chart. * * @return Menu|null */ public function getChartMenu(Individual $individual) { return new Menu($this->getTitle(), 'timeline.php?pids%5B%5D=' . $individual->getXref() . '&ged=' . $individual->getTree()->getNameUrl(), 'menu-chart-timeline', array('rel' => 'nofollow')); }