summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Controller/RelationshipController.php305
-rw-r--r--composer.json1
-rw-r--r--composer.lock48
-rw-r--r--includes/functions/functions.php143
-rw-r--r--relationship.php479
-rw-r--r--themes/clouds/css-1.7.0/style.css9
-rw-r--r--themes/colors/css-1.7.0/style.css9
-rw-r--r--themes/fab/css-1.7.0/style.css9
-rw-r--r--themes/minimal/css-1.7.0/style.css9
-rw-r--r--themes/webtrees/css-1.7.0/style.css9
-rw-r--r--themes/xenea/css-1.7.0/style.css9
-rw-r--r--vendor/autoload.php2
-rw-r--r--vendor/composer/autoload_classmap.php2
-rw-r--r--vendor/composer/autoload_psr4.php1
-rw-r--r--vendor/composer/autoload_real.php10
-rw-r--r--vendor/composer/installed.json48
16 files changed, 350 insertions, 743 deletions
diff --git a/app/Controller/RelationshipController.php b/app/Controller/RelationshipController.php
index 9eeb4b0296..9b300d7ca6 100644
--- a/app/Controller/RelationshipController.php
+++ b/app/Controller/RelationshipController.php
@@ -16,264 +16,107 @@ namespace Fisharebest\Webtrees;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+use Fisharebest\Algorithm\Dijkstra;
+
/**
- * Class BranchesController - Controller for the branches list
+ * Class RelationshipController - Controller for the relationships calculations
*/
-class BranchesController extends PageController {
- /** @var string Generate the branches for this surname */
- private $surname;
-
- /** @var bool Whether to use Standard phonetic matching */
- private $soundex_std;
-
- /** @var bool Whether to use Daitch-Mokotov phonetic matching */
- private $soundex_dm;
-
- /** @var Individual[] Everyone with the selected surname */
- private $individuals = array();
-
- /** @var Individual[] Ancestors of the root person - for SOSA numbers */
- private $ancestors = array();
-
- /**
- * Create a branches list controller
- */
- public function __construct() {
- parent::__construct();
-
- $this->surname = Filter::get('surname');
- $this->soundex_std = Filter::getBool('soundex_std');
- $this->soundex_dm = Filter::getBool('soundex_dm');
-
- if ($this->surname) {
- $this->setPageTitle(/* I18N: %s is a surname */
- I18N::translate('Branches of the %s family', Filter::escapeHtml($this->surname)));
- $this->loadIndividuals();
- $self = Individual::getInstance(WT_USER_GEDCOM_ID);
- if ($self) {
- $this->loadAncestors($self, 1);
- }
- } else {
- $this->setPageTitle(/* I18N: Branches of a family tree */ I18N::translate('Branches'));
- }
- }
-
- /**
- * The surname to be used on this page.
- *
- * @return null|string
- */
- public function getSurname() {
- return $this->surname;
- }
-
+class RelationshipController extends PageController {
/**
- * Should we use Standard phonetic matching
+ * Calculate the shortest paths - or all paths - between two individuals.
*
- * @return boolean
- */
- public function getSoundexStd() {
- return $this->soundex_std;
- }
-
- /**
- * Should we use Daitch-Mokotov phonetic matching
+ * @param Individual $indi1
+ * @param Individual $indi2
+ * @param boolean $all;
*
- * @return boolean
+ * @return string[][];
*/
- public function getSoundexDm() {
- return $this->soundex_dm;
- }
+ public function calculateRelationships(Individual $individual1, Individual $individual2, $all) {
+ $rows = Database::prepare(
+ "SELECT l_from, l_to FROM `##link` WHERE l_file = :tree_id AND l_type IN ('FAMS', 'FAMC', 'CHIL', 'HUSB', 'WIFE')"
+ )->execute(array(
+ 'tree_id' => $individual1->getTree()->getTreeId()
+ ))->fetchAll();
- /**
- * Fetch all individuals with a matching surname
- */
- private function loadIndividuals() {
- $sql =
- "SELECT DISTINCT i_id AS xref, i_file AS gedcom_id, i_gedcom AS gedcom" .
- " FROM `##individuals`" .
- " JOIN `##name` ON (i_id=n_id AND i_file=n_file)" .
- " WHERE n_file = ?" .
- " AND n_type != ?" .
- " AND (n_surn = ? OR n_surname = ?";
- $args = array(WT_GED_ID, '_MARNM', $this->surname, $this->surname);
- if ($this->soundex_std) {
- $sdx = Soundex::russell($this->surname);
- if ($sdx) {
- foreach (explode(':', $sdx) as $value) {
- $sql .= " OR n_soundex_surn_std LIKE CONCAT('%', ?, '%')";
- $args[] = $value;
- }
- }
- }
- if ($this->soundex_dm) {
- $sdx = Soundex::daitchMokotoff($this->surname);
- if ($sdx) {
- foreach (explode(':', $sdx) as $value) {
- $sql .= " OR n_soundex_surn_dm LIKE CONCAT('%', ?, '%')";
- $args[] = $value;
- }
- }
- }
- $sql .= ')';
- $rows = Database::prepare($sql)->execute($args)->fetchAll();
- $this->individuals = array();
+ $graph = array();
foreach ($rows as $row) {
- $this->individuals[] = Individual::getInstance($row->xref, $row->gedcom_id, $row->gedcom);
+ $graph[$row->l_from][$row->l_to] = 1;
}
- // Sort by birth date, oldest first
- usort($this->individuals, __NAMESPACE__ . '\Individual::compareBirthDate');
- }
- /**
- * Load the ancestors of an individual, so we can highlight them in the list
- *
- * @param Individual $ancestor
- * @param integer $sosa
- */
- private function loadAncestors(Individual $ancestor, $sosa) {
- if ($ancestor) {
- $this->ancestors[$sosa] = $ancestor;
- foreach ($ancestor->getChildFamilies() as $family) {
- foreach ($family->getSpouses() as $parent) {
- $this->loadAncestors($parent, $sosa * 2 + ($parent->getSex() == 'F' ? 1 : 0));
- }
- }
- }
- }
+ $xref1 = $individual1->getXref();
+ $xref2 = $individual2->getXref();
+ $dijkstra = new Dijkstra($graph);
+ $paths = $dijkstra->shortestPaths($xref1, $xref2);
- /**
- * For each individual with no ancestors, list their descendants.
- *
- * @return string
- */
- public function getPatriarchsHtml() {
- $html = '';
- foreach ($this->individuals as $individual) {
- foreach ($individual->getChildFamilies() as $family) {
- foreach ($family->getSpouses() as $parent) {
- if (in_array($parent, $this->individuals, true)) {
- continue 3;
+ if ($all) {
+ $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'];
+ $exclude[] = $next['path'][$n];
+ // Add any new path to the queue
+ foreach ($dijkstra->shortestPaths($xref1, $xref2, $exclude) as $new_path) {
+ $queue[] = array('path' => $new_path, 'exclude' => $exclude);
+ }
}
}
}
- $html .= $this->getDescendantsHtml($individual);
+ // Extract the paths from the queue, removing duplicates.
+ $paths = array();
+ foreach ($queue as $next) {
+ $paths[implode('-', $next['path'])] = $next['path'];
+ }
}
- return $html;
+ return $paths;
}
/**
- * Generate a recursive list of descendants of an individual.
- * If parents are specified, we can also show the pedigree (adopted, etc.).
+ * Convert a path (list of XREFs) to an "old-style" string of relationships.
+ *
+ * Return an empty array, if privacy rules prevent us viewing any node.
*
- * @param Individual $individual
- * @param Family|null $parents
+ * @param string $path
*
- * @return string
+ * @return array();
*/
- private function getDescendantsHtml(Individual $individual, Family $parents = null) {
- // A person has many names. Select the one that matches the searched surname
- $person_name = '';
- foreach ($individual->getAllNames() as $name) {
- list($surn1) = explode(",", $name['sort']);
- if (// one name is a substring of the other
- stripos($surn1, $this->surname) !== false ||
- stripos($this->surname, $surn1) !== false ||
- // one name sounds like the other
- $this->soundex_std && Soundex::compare(Soundex::russell($surn1), Soundex::russell($this->surname)) ||
- $this->soundex_dm && Soundex::compare(Soundex::daitchMokotoff($surn1), Soundex::daitchMokotoff($this->surname))
- ) {
- $person_name = $name['full'];
- break;
- }
- }
-
- // No matching name? Typically children with a different surname. The branch stops here.
- if (!$person_name) {
- return '<li title="' . strip_tags($individual->getFullName()) . '">' . $individual->getSexImage() . '…</li>';
- }
-
- // Is this individual one of our ancestors?
- $sosa = array_search($individual, $this->ancestors, true);
- if ($sosa) {
- $sosa_class = 'search_hit';
- $sosa_html = ' <a class="details1 ' . $individual->getBoxStyle() . '" title="' . I18N::translate('Sosa') . '" href="relationship.php?pid2=' . WT_USER_ROOT_ID . '&amp;pid1=' . $individual->getXref() . '">' . $sosa . '</a>' . self::sosaGeneration($sosa);
- } else {
- $sosa_class = '';
- $sosa_html = '';
- }
+ public function oldStyleRelationshipPath(array $path) {
+ $spouse_codes = array('M' => 'hus', 'F' => 'wif', 'U' => 'spo');
+ $parent_codes = array('M' => 'fat', 'F' => 'mot', 'U' => 'par');
+ $child_codes = array('M' => 'son', 'F' => 'dau', 'U' => 'chi');
+ $sibling_codes = array('M' => 'bro', 'F' => 'sis', 'U' => 'sib');
+ $relationships = array();
- // Generate HTML for this individual, and all their descendants
- $indi_html = $individual->getSexImage() . '<a class="' . $sosa_class . '" href="' . $individual->getHtmlUrl() . '">' . $person_name . '</a> ' . $individual->getLifeSpan() . $sosa_html;
-
- // If this is not a birth pedigree (e.g. an adoption), highlight it
- if ($parents) {
- $pedi = '';
- foreach ($individual->getFacts('FAMC') as $fact) {
- if ($fact->getTarget() === $parents) {
- $pedi = $fact->getAttribute('PEDI');
- break;
- }
+ for ($i = 1; $i < count($path); $i += 2) {
+ $family = Family::getInstance($path[$i]);
+ $prev = Individual::getInstance($path[$i - 1]);
+ $next = Individual::getInstance($path[$i + 1]);
+ if (preg_match('/\n\d (HUSB|WIFE|CHIL) @' . $prev->getXref() . '@/', $family->getGedcom(), $match)) {
+ $rel1 = $match[1];
+ } else {
+ return array();
}
- if ($pedi && $pedi != 'birth') {
- $indi_html = '<span class="red">' . WT_Gedcom_Code_Pedi::getValue($pedi, $individual) . '</span> ' . $indi_html;
+ if (preg_match('/\n\d (HUSB|WIFE|CHIL) @' . $next->getXref() . '@/', $family->getGedcom(), $match)) {
+ $rel2 = $match[1];
+ } else {
+ return array();
}
- }
-
- // spouses and children
- $spouse_families = $individual->getSpouseFamilies();
- if ($spouse_families) {
- usort($spouse_families, __NAMESPACE__ . '\Family::compareMarrDate');
- $fam_html = '';
- foreach ($spouse_families as $family) {
- $fam_html .= $indi_html; // Repeat the individual details for each spouse.
-
- $spouse = $family->getSpouse($individual);
- if ($spouse) {
- $sosa = array_search($spouse, $this->ancestors, true);
- if ($sosa) {
- $sosa_class = 'search_hit';
- $sosa_html = ' <a class="details1 ' . $spouse->getBoxStyle() . '" title="' . I18N::translate('Sosa') . '" href="relationship.php?pid2=' . WT_USER_ROOT_ID . '&amp;pid1=' . $spouse->getXref() . '"> ' . $sosa . ' </a>' . self::sosaGeneration($sosa);
- } else {
- $sosa_class = '';
- $sosa_html = '';
- }
- $marriage_year = $family->getMarriageYear();
- if ($marriage_year) {
- $fam_html .= ' <a href="' . $family->getHtmlUrl() . '" title="' . strip_tags($family->getMarriageDate()->display()) . '"><i class="icon-rings"></i>' . $marriage_year . '</a>';
- } elseif ($family->getFirstFact('MARR')) {
- $fam_html .= ' <a href="' . $family->getHtmlUrl() . '" title="' . WT_Gedcom_Tag::getLabel('MARR') . '"><i class="icon-rings"></i></a>';
- } elseif ($family->getFirstFact('_NMR')) {
- $fam_html .= ' <a href="' . $family->getHtmlUrl() . '" title="' . WT_Gedcom_Tag::getLabel('_NMR') . '"><i class="icon-rings"></i></a>';
- }
- $fam_html .= ' ' . $spouse->getSexImage() . '<a class="' . $sosa_class . '" href="' . $spouse->getHtmlUrl() . '">' . $spouse->getFullName() . '</a> ' . $spouse->getLifeSpan() . ' ' . $sosa_html;
- }
-
- $fam_html .= '<ol>';
- foreach ($family->getChildren() as $child) {
- $fam_html .= $this->getDescendantsHtml($child, $family);
- }
- $fam_html .= '</ol>';
+ if (($rel1 === 'HUSB' || $rel1 === 'WIFE') && ($rel2 === 'HUSB' || $rel2 === 'WIFE')) {
+ $relationships[$i] = $spouse_codes[$next->getSex()];
+ } elseif (($rel1 === 'HUSB' || $rel1 === 'WIFE') && $rel2 === 'CHIL') {
+ $relationships[$i] = $child_codes[$next->getSex()];
+ } elseif ($rel1 === 'CHIL' && ($rel2 === 'HUSB' || $rel2 === 'WIFE')) {
+ $relationships[$i] = $parent_codes[$next->getSex()];
+ } elseif ($rel1 === 'CHIL' && $rel2 === 'CHIL') {
+ $relationships[$i] = $sibling_codes[$next->getSex()];
}
-
- return '<li>' . $fam_html . '</li>';
- } else {
- // No spouses - just show the individual
- return '<li>' . $indi_html . '</li>';
}
- }
-
- /**
- * Convert a SOSA number into a generation number. e.g. 8 = great-grandfather = 3 generations
- *
- * @param integer $sosa
- *
- * @return string
- */
- private static function sosaGeneration($sosa) {
- $generation = (int) log($sosa, 2) + 1;
- return '<sup title="' . I18N::translate('Generation') . '">' . $generation . '</sup>';
+ return $relationships;
}
}
diff --git a/composer.json b/composer.json
index c94e501bb9..6eb9e2d2c5 100644
--- a/composer.json
+++ b/composer.json
@@ -25,6 +25,7 @@
"require": {
"bombayworks/zendframework1": "*",
"ezyang/htmlpurifier": "*",
+ "fisharebest/algorithm": "*",
"fisharebest/ext-calendar": "*",
"ircmaxell/password-compat": "*",
"michelf/php-markdown": "*",
diff --git a/composer.lock b/composer.lock
index 0e0b8cf3bb..587ca64c9e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "542685d0ebe4107f510791dcc2f67542",
+ "hash": "32d402bd09151198895b11be00e60610",
"packages": [
{
"name": "bombayworks/zendframework1",
@@ -103,6 +103,52 @@
"time": "2013-11-30 08:25:19"
},
{
+ "name": "fisharebest/algorithm",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fisharebest/algorithm.git",
+ "reference": "ddfbf31e7b1ef49f227409a41261770ed3fb467b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fisharebest/algorithm/zipball/ddfbf31e7b1ef49f227409a41261770ed3fb467b",
+ "reference": "ddfbf31e7b1ef49f227409a41261770ed3fb467b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "satooshi/php-coveralls": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fisharebest\\Algorithm\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-3.0+"
+ ],
+ "authors": [
+ {
+ "name": "Greg Roach",
+ "email": "greg@subaqua.co.uk",
+ "role": "Developer"
+ }
+ ],
+ "description": "Implementation of standard algorithms in PHP.",
+ "homepage": "https://github.com/fisharebest/algorithm",
+ "keywords": [
+ "Algorithm",
+ "dijkstra"
+ ],
+ "time": "2015-02-15 21:38:53"
+ },
+ {
"name": "fisharebest/ext-calendar",
"version": "1.3.0",
"source": {
diff --git a/includes/functions/functions.php b/includes/functions/functions.php
index 58c9b46bdc..0df71a0785 100644
--- a/includes/functions/functions.php
+++ b/includes/functions/functions.php
@@ -255,8 +255,6 @@ function sort_facts(&$arr) {
/**
* For close family relationships, such as the families tab and the family navigator
* Display a tick if both individuals are the same.
- * Stop after 3 steps, because pending edits may mean that there is no longer a
- * relationship to find.
*
* @param Individual $person1
* @param Individual $person2
@@ -267,7 +265,7 @@ function get_close_relationship_name(Individual $person1, Individual $person2) {
if ($person1 === $person2) {
$label = '<i class="icon-selected" title="' . I18N::translate('self') . '"></i>';
} else {
- $label = get_relationship_name(get_relationship($person1, $person2, true, 3));
+ $label = get_relationship_name(get_relationship($person1, $person2));
}
return $label;
@@ -275,8 +273,6 @@ function get_close_relationship_name(Individual $person1, Individual $person2) {
/**
* For facts on the individual/family pages.
- * Stop after 4 steps, as distant relationships may take a long time to find.
- * Review the limit of 4 if/when the performance of the function is improved.
*
* @param Individual $person1
* @param Individual $person2
@@ -287,7 +283,7 @@ function get_associate_relationship_name(Individual $person1, Individual $person
if ($person1 === $person2) {
$label = I18N::translate('self');
} else {
- $label = get_relationship_name(get_relationship($person1, $person2, true, 4));
+ $label = get_relationship_name(get_relationship($person1, $person2));
}
return $label;
@@ -296,19 +292,22 @@ function get_associate_relationship_name(Individual $person1, Individual $person
/**
* Get relationship between two individuals in the gedcom
*
- * @param Individual $person1 the person to compute the relationship from
- * @param Individual $person2 the person to compute the relatiohip to
- * @param boolean $followspouse whether to add spouses to the path
- * @param integer $maxlength the maximum length of path
- * @param integer $path_to_find which path in the relationship to find, 0 is the shortest path, 1 is the next shortest path, etc
+ * @param Individual $person1 The person to compute the relationship from
+ * @param Individual $person2 The person to compute the relatiohip to
+ * @param integer $maxlength The maximum length of path
*
* @return array|bool An array of nodes on the relationship path, or false if no path found
*/
-function get_relationship(Individual $person1, Individual $person2, $followspouse = true, $maxlength = 0, $path_to_find = 0) {
+function get_relationship(Individual $person1, Individual $person2, $maxlength = 4) {
if ($person1 === $person2) {
return false;
}
+ $spouse_codes = array('M' => 'hus', 'F' => 'wif', 'U' => 'spo');
+ $parent_codes = array('M' => 'fat', 'F' => 'mot', 'U' => 'par');
+ $child_codes = array('M' => 'son', 'F' => 'dau', 'U' => 'chi');
+ $sibling_codes = array('M' => 'bro', 'F' => 'sis', 'U' => 'sib');
+
//-- current path nodes
$p1nodes = array();
//-- ids visited
@@ -354,15 +353,11 @@ function get_relationship(Individual $person1, Individual $person2, $followspous
$node1['length']++;
$node1['path'][] = $spouse;
$node1['indi'] = $spouse;
- $node1['relations'][] = 'parent';
+ $node1['relations'][] = $parent_codes[$spouse->getSex()];
$p1nodes[] = $node1;
if ($spouse === $person2) {
- if ($path_to_find > 0) {
- $path_to_find--;
- } else {
- $found = true;
- $resnode = $node1;
- }
+ $found = true;
+ $resnode = $node1;
} else {
$visited[$spouse->getXref()] = true;
}
@@ -374,15 +369,11 @@ function get_relationship(Individual $person1, Individual $person2, $followspous
$node1['length']++;
$node1['path'][] = $child;
$node1['indi'] = $child;
- $node1['relations'][] = 'sibling';
+ $node1['relations'][] = $sibling_codes[$child->getSex()];
$p1nodes[] = $node1;
if ($child === $person2) {
- if ($path_to_find > 0) {
- $path_to_find--;
- } else {
- $found = true;
- $resnode = $node1;
- }
+ $found = true;
+ $resnode = $node1;
} else {
$visited[$child->getXref()] = true;
}
@@ -392,25 +383,19 @@ function get_relationship(Individual $person1, Individual $person2, $followspous
//-- check all spouses and children of this node
foreach ($indi->getSpouseFamilies(WT_PRIV_HIDE) as $family) {
$visited[$family->getXref()] = true;
- if ($followspouse) {
- foreach ($family->getSpouses(WT_PRIV_HIDE) as $spouse) {
- if (!in_array($spouse->getXref(), $node1) || !isset($visited[$spouse->getXref()])) {
- $node1 = $node;
- $node1['length']++;
- $node1['path'][] = $spouse;
- $node1['indi'] = $spouse;
- $node1['relations'][] = 'spouse';
- $p1nodes[] = $node1;
- if ($spouse === $person2) {
- if ($path_to_find > 0) {
- $path_to_find--;
- } else {
- $found = true;
- $resnode = $node1;
- }
- } else {
- $visited[$spouse->getXref()] = true;
- }
+ foreach ($family->getSpouses(WT_PRIV_HIDE) as $spouse) {
+ if (!in_array($spouse->getXref(), $node1) || !isset($visited[$spouse->getXref()])) {
+ $node1 = $node;
+ $node1['length']++;
+ $node1['path'][] = $spouse;
+ $node1['indi'] = $spouse;
+ $node1['relations'][] = $spouse_codes[$spouse->getSex()];
+ $p1nodes[] = $node1;
+ if ($spouse === $person2) {
+ $found = true;
+ $resnode = $node1;
+ } else {
+ $visited[$spouse->getXref()] = true;
}
}
}
@@ -420,15 +405,11 @@ function get_relationship(Individual $person1, Individual $person2, $followspous
$node1['length']++;
$node1['path'][] = $child;
$node1['indi'] = $child;
- $node1['relations'][] = 'child';
+ $node1['relations'][] = $child_codes[$child->getSex()];
$p1nodes[] = $node1;
if ($child === $person2) {
- if ($path_to_find > 0) {
- $path_to_find--;
- } else {
- $found = true;
- $resnode = $node1;
- }
+ $found = true;
+ $resnode = $node1;
} else {
$visited[$child->getXref()] = true;
}
@@ -439,52 +420,6 @@ function get_relationship(Individual $person1, Individual $person2, $followspous
unset($p1nodes[$shortest]);
}
- // Convert generic relationships into sex-specific ones.
- foreach ($resnode['path'] as $n => $indi) {
- switch ($resnode['relations'][$n]) {
- case 'parent':
- switch ($indi->getSex()) {
- case 'M':
- $resnode['relations'][$n] = 'father';
- break;
- case 'F':
- $resnode['relations'][$n] = 'mother';
- break;
- }
- break;
- case 'child':
- switch ($indi->getSex()) {
- case 'M':
- $resnode['relations'][$n] = 'son';
- break;
- case 'F':
- $resnode['relations'][$n] = 'daughter';
- break;
- }
- break;
- case 'spouse':
- switch ($indi->getSex()) {
- case 'M':
- $resnode['relations'][$n] = 'husband';
- break;
- case 'F':
- $resnode['relations'][$n] = 'wife';
- break;
- }
- break;
- case 'sibling':
- switch ($indi->getSex()) {
- case 'M':
- $resnode['relations'][$n] = 'brother';
- break;
- case 'F':
- $resnode['relations'][$n] = 'sister';
- break;
- }
- break;
- }
- }
-
return $resnode;
}
@@ -518,13 +453,11 @@ function get_relationship_name($nodes) {
// This is very repetitive in English, but necessary in order to handle the
// complexities of other languages.
- // Make each relationship parts the same length, for simpler matching.
- $combined_path = '';
- foreach ($path as $rel) {
- $combined_path .= substr($rel, 0, 3);
- }
-
- return get_relationship_name_from_path($combined_path, $person1, $person2);
+ return get_relationship_name_from_path(
+ implode('', array_slice($nodes['relations'], 1)),
+ $nodes['path'][0],
+ $nodes['path'][count($nodes['path']) - 1]
+ );
}
/**
diff --git a/relationship.php b/relationship.php
index 4955770f90..8aed0f4445 100644
--- a/relationship.php
+++ b/relationship.php
@@ -20,35 +20,17 @@ namespace Fisharebest\Webtrees;
* Defined in session.php
*
* @global Tree $WT_TREE
- * @global integer $bwidth
*/
-global $WT_TREE, $bwidth;
+global $WT_TREE;
define('WT_SCRIPT_NAME', 'relationship.php');
require './includes/session.php';
-$controller = new PageController;
-
-$pid1 = Filter::get('pid1', WT_REGEX_XREF);
-$pid2 = Filter::get('pid2', WT_REGEX_XREF);
-$show_full = Filter::getInteger('show_full', 0, 1, $WT_TREE->getPreference('PEDIGREE_FULL_DETAILS'));
-$path_to_find = Filter::getInteger('path_to_find');
-$followspouse = Filter::getBool('followspouse');
-$asc = Filter::getBool('asc');
-
-$asc = $asc ? -1 : 1;
-$Dbwidth = $bwidth;
-if (!$show_full) {
- $bwidth = Theme::theme()->parameter('compact-chart-box-x');
- $bheight = Theme::theme()->parameter('compact-chart-box-y');
- $Dbwidth = Theme::theme()->parameter('compact-chart-box-x');
-}
-
-$Dbheight = $bheight;
-$Dbxspacing = 0;
-$Dbyspacing = 0;
-$Dbasexoffset = 0;
-$Dbaseyoffset = 0;
+$controller = new RelationshipController;
+$pid1 = Filter::get('pid1', WT_REGEX_XREF);
+$pid2 = Filter::get('pid2', WT_REGEX_XREF);
+$show_full = Filter::getInteger('show_full', 0, 1, $WT_TREE->getPreference('PEDIGREE_FULL_DETAILS'));
+$find_all = Filter::getInteger('find_all', 0, 1);
$person1 = Individual::getInstance($pid1);
$person2 = Individual::getInstance($pid2);
@@ -57,355 +39,160 @@ $controller
->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
->addInlineJavascript('autocomplete();');
-if ($person1 && $person1->canShowName() && $person2 && $person2->canShowName()) {
+if ($person1 && $person2) {
$controller
->setPageTitle(I18N::translate(/* I18N: %s are individual’s names */ 'Relationships between %1$s and %2$s', $person1->getFullName(), $person2->getFullName()))
->PageHeader();
- $node = get_relationship($person1, $person2, $followspouse, 0, $path_to_find);
- // If no blood relationship exists, look for relationship via marriage
- if ($path_to_find == 0 && $node == false && $followspouse == false) {
- $followspouse = true;
- $node = get_relationship($person1, $person2, $followspouse, 0, $path_to_find);
- }
- $disp = true;
+ $paths = $controller->calculateRelationships($person1, $person2, $find_all);
} else {
$controller
->setPageTitle(I18N::translate('Relationships'))
->PageHeader();
- $node = false;
- $disp = false;
+ $paths = array();
}
?>
-<div id="relationship-page">
- <h2><?php echo $controller->getPageTitle(); ?></h2>
- <form name="people" method="get" action="?">
- <input type="hidden" name="ged" value="<?php echo Filter::escapeHtml(WT_GEDCOM); ?>">
- <input type="hidden" name="path_to_find" value="0">
- <table class="list_table">
- <tr>
- <td colspan="2" class="topbottombar center">
- <?php echo I18N::translate('Relationships'); ?>
- </td>
- <td colspan="2" class="topbottombar center">
- <?php echo I18N::translate('Options:'); ?>
- </td>
- </tr>
- <tr>
- <td class="descriptionbox">
- <?php echo I18N::translate('Individual 1'); ?>
- </td>
- <td class="optionbox vmiddle">
- <input tabindex="1" class="pedigree_form" data-autocomplete-type="INDI" type="text" name="pid1" id="pid1" size="3" value="<?php echo $pid1; ?>">
- <?php echo print_findindi_link('pid1'); ?>
- </td>
- <td class="descriptionbox">
- <?php echo I18N::translate('Show details'); ?>
- </td>
- <td class="optionbox vmiddle">
+<h2><?php echo $controller->getPageTitle(); ?></h2>
+<form name="people" method="get" action="?">
+ <input type="hidden" name="ged" value="<?php echo Filter::escapeHtml(WT_GEDCOM); ?>">
+ <table class="list_table">
+ <tr>
+ <td class="descriptionbox">
+ <?php echo I18N::translate('Individual 1'); ?>
+ </td>
+ <td class="optionbox">
+ <input class="pedigree_form" data-autocomplete-type="INDI" type="text" name="pid1" id="pid1" size="3" value="<?php echo $pid1; ?>">
+ <?php echo print_findindi_link('pid1'); ?>
+ </td>
+ <td class="optionbox">
+ <label>
<?php echo two_state_checkbox('show_full', $show_full); ?>
- </td>
- </tr>
- <tr>
- <td class="descriptionbox">
- <?php echo I18N::translate('Individual 2'); ?>
- </td>
- <td class="optionbox vmiddle">
- <input tabindex="2" class="pedigree_form" data-autocomplete-type="INDI" type="text" name="pid2" id="pid2" size="3" value="<?php echo $pid2; ?>">
- <?php echo print_findindi_link('pid2'); ?>
- </td>
- <td class="descriptionbox">
- <?php echo I18N::translate('Show oldest top'); ?>
- </td>
- <td class="optionbox">
- <input tabindex="4" type="checkbox" name="asc" value="1" <?php echo $asc === -1 ? 'checked' : ''; ?>>
- </td>
- </tr>
- <tr>
- <td class="descriptionbox">
- <?php
- if ($path_to_find > 0) {
- echo I18N::translate('Show path');
- }
- ?>
- </td>
- <td class="optionbox">
- <?php
- for ($i = 0; $i < $path_to_find; ++$i) {
- echo ' <a href="relationship.php?pid1=', $pid1, '&amp;pid2=', $pid2, '&amp;path_to_find=', $i, '&amp;followspouse=', $followspouse, '&amp;show_full=', $show_full, '&amp;asc=', -$asc, '">', $i + 1, '</a>';
- }
- ?>
- </td>
- <td class="descriptionbox">
- <?php echo I18N::translate('Check relationships by marriage'), help_link('CHECK_MARRIAGE_RELATIONS'); ?>
- </td>
- <td class="optionbox" id="followspousebox">
- <input tabindex="6" type="checkbox" name="followspouse" value="1" <?php echo $followspouse ? 'checked' : ''; ?> onclick="document.people.path_to_find.value='-1';" >
- </td>
- </tr>
- <td class="topbottombar vmiddle center" colspan="2">
- <?php
- if ($node) {
- echo '<input type="submit" value="', I18N::translate('Find next path'), '" onclick="document.people.path_to_find.value=', $path_to_find + 1, ';">';
- echo help_link('next_path');
- }
- ?>
- </td>
- <td class="topbottombar vmiddle center" colspan="2">
- <input tabindex="7" type="submit" value="<?php echo I18N::translate('View'); ?>">
- </td>
- </tr>
- </table>
- </form>
-
+ <?php echo I18N::translate('Show details'); ?>
+ </label>
+ </td>
+ <td class="optionbox vmiddle" rowspan="2">
+ <input type="submit" value="<?php echo I18N::translate('View'); ?>">
+ </td>
+ </tr>
+ <tr>
+ <td class="descriptionbox">
+ <?php echo I18N::translate('Individual 2'); ?>
+ </td>
+ <td class="optionbox">
+ <input class="pedigree_form" data-autocomplete-type="INDI" type="text" name="pid2" id="pid2" size="3" value="<?php echo $pid2; ?>">
+ <?php echo print_findindi_link('pid2'); ?>
+ <br>
+ <a href="#" onclick="var x = jQuery('#pid1').val(); jQuery('#pid1').val(jQuery('#pid2').val()); jQuery('#pid2').val(x); return false;"><?php /* I18N: Reverse the order of two individuals */ echo I18N::translate('Swap individuals'); ?></a>
+ </td>
+ <td class="optionbox">
+ <label>
+ <input type="radio" name="find_all" value="0" <?php echo $find_all ? '' : 'checked'; ?>>
+ <?php echo I18N::translate('Find the shortest relationships'); ?>
+ </label>
+ <br>
+ <label>
+ <input type="radio" name="find_all" value="1"<?php echo $find_all ? 'checked' : ''; ?>>
+ <?php echo I18N::translate('Find all possible relationships'); ?>
+ </label>
+ </td>
+ </tr>
+ </table>
+</form>
<?php
-$maxyoffset = $Dbaseyoffset;
if ($person1 && $person2) {
- if (!$disp) {
- echo '<div class="error">', I18N::translate('This information is private and cannot be shown.'), '</div>';
- } elseif (!$node) {
- if ($path_to_find == 0) {
- echo '<p class="error">', I18N::translate('No link between the two individuals could be found.'), '</p>';
- } else {
- echo '<p class="error">', I18N::translate('No other link between the two individuals could be found.'), '</p>';
- }
+ if (I18N::direction() === 'ltr') {
+ $horizontal_arrow = '<br><i class="icon-rarrow"></i>';
} else {
- if ($node) {
- echo '<h3>', I18N::translate('Relationship: %s', get_relationship_name($node)), '</h3>';
+ $horizontal_arrow = '<br><i class="icon-larrow"></i>';
+ }
+ $up_arrow = ' <i class="icon-uarrow"></i>';
+ $down_arrow = ' <i class="icon-darrow"></i>';
- // Use relative layout to position the person boxes.
- echo '<div id="relationship_chart" style="position:relative;">';
+ $num_paths = 0;
+ foreach ($paths as $path) {
+ // Extract the relationship names between pairs of individuals
+ $relationships = $controller->oldStyleRelationshipPath($path);
+ if (!$relationships) {
+ echo "<h3>private or error</h3>";
+ continue;
+ }
+ echo '<h3>', I18N::translate('Relationship: %s', get_relationship_name_from_path(implode('', $relationships), $person1, $person2)), '</h3>';
+ $num_paths++;
- $yoffset = $Dbaseyoffset + 20;
- $xoffset = $Dbasexoffset;
- $colNum = 0;
- $rowNum = 0;
- $previous = '';
- $change_count = ''; // shift right on alternate change of direction
- $xs = $Dbxspacing + 70;
- $ys = $Dbyspacing + 50;
- // step1 = tree depth calculation
- $dmin = 0;
- $dmax = 0;
- $depth = 0;
- foreach ($node['path'] as $index=>$person) {
- if ($node['relations'][$index] === 'father' || $node['relations'][$index] === 'mother' || $node['relations'][$index] === 'parent') {
- $depth++;
- if ($depth > $dmax) {
- $dmax = $depth;
- }
- if ($asc == 0) {
- $asc = 1; // the first link is a parent link
- }
- }
- if ($node['relations'][$index] === 'son' || $node['relations'][$index] === 'daughter' || $node['relations'][$index] === 'child') {
- $depth--;
- if ($depth < $dmin) {
- $dmin = $depth;
- }
- if ($asc === 0) {
- $asc = -1; // the first link is a child link
- }
- }
- }
- $depth = $dmax + $dmin;
- // need more yoffset before the first box ?
- if ($asc == 1) {
- $yoffset -= $dmin * ($Dbheight + $ys);
- }
- if ($asc == -1) {
- $yoffset += $dmax * ($Dbheight + $ys);
- }
- $rowNum = ($asc == -1) ? $depth : 0;
- $maxxoffset = -1 * $Dbwidth - 20;
- $maxyoffset = $yoffset;
- // Left and right get reversed on RTL pages
- if (I18N::direction() === 'ltr') {
- $right_arrow = 'icon-rarrow';
- } else {
- $right_arrow = 'icon-larrow';
- }
- // Up and down get reversed, for the “oldest at top” option
- if ($asc == 1) {
- $up_arrow = 'icon-uarrow';
- $down_arrow = 'icon-darrow';
- } else {
- $up_arrow = 'icon-darrow';
- $down_arrow = 'icon-uarrow';
- }
- foreach ($node['path'] as $index=>$person) {
- $linex = $xoffset;
- $liney = $yoffset;
- switch ($person->getSex()) {
- case 'M':
- $mfstyle = '';
- break;
- case 'F':
- $mfstyle = 'F';
- break;
- default:
- $mfstyle = 'NN';
- break;
- }
- switch ($node['relations'][$index]) {
- case 'father':
- case 'mother':
- case 'parent':
- $arrow_img = $down_arrow;
- $line = Theme::theme()->parameter('image-vline');
- $liney += $Dbheight;
- $linex += $Dbwidth / 2;
- $lh = 54;
- $lw = 3;
- $lh = $ys;
- $linex = $xoffset + $Dbwidth / 2;
- // put the box up or down ?
- $yoffset += $asc * ($Dbheight + $lh);
- $rowNum += $asc;
- if ($asc == 1) {
- $liney = $yoffset - $lh;
- } else {
- $liney = $yoffset + $Dbheight;
- }
- // need to draw a joining line ?
- if ($previous === 'child' && ($change_count++ % 2) === 0) {
- $joinh = 3;
- $joinw = $xs / 2 + 2;
- $xoffset += $Dbwidth + $xs;
- $colNum++;
- //$rowNum is inherited from the box immediately to the left
- $linex = $xoffset - $xs / 2;
- if ($asc == -1) {
- $liney = $yoffset + $Dbheight;
- } else {
- $liney = $yoffset - $lh;
- }
- $joinx = $xoffset - $xs;
- $joiny = $liney - 2 - ($asc - 1) / 2 * $lh;
- echo "<div id=\"joina", $index, "\" style=\"position:absolute; ", I18N::direction() === 'ltr' ? 'left' : 'right', ':', $joinx + $Dbxspacing, 'px; top:', $joiny + $Dbyspacing, "px;\" align=\"center\"><img src=\"", Theme::theme()->parameter('image-hline'), "\" align=\"left\" width=\"", $joinw, "\" height=\"", $joinh, "\" alt=\"\"></div>";
- $joinw = $xs / 2 + 2;
- $joinx = $joinx + $xs / 2;
- $joiny = $joiny + $asc * $lh;
- echo "<div id=\"joinb", $index, "\" style=\"position:absolute; ", I18N::direction() === 'ltr' ? 'left' : 'right', ':', $joinx + $Dbxspacing, 'px; top:', $joiny + $Dbyspacing, "px;\" align=\"center\"><img src=\"", Theme::theme()->parameter('image-hline'), "\" align=\"left\" width=\"", $joinw, "\" height=\"", $joinh, "\" alt=\"\"></div>";
- } else {
- $change_count = '';
- }
- $previous = 'parent';
- break;
- case 'brother':
- case 'sister':
- case 'sibling':
- case 'husband':
- case 'wife':
- case 'spouse':
- $arrow_img = $right_arrow;
- $xoffset += $Dbwidth + $Dbxspacing + 70;
- $colNum++;
- //$rowNum is inherited from the box immediately to the left
- $line = Theme::theme()->parameter('image-hline');
- $linex += $Dbwidth;
- $liney += $Dbheight / 2;
- $lh = 3;
- $lw = 70;
- $lw = $xs;
- $linex = $xoffset - $lw;
- $liney = $yoffset + $Dbheight / 4;
- $previous = '';
+ // Use a table/grid for layout.
+ $table = array();
+ // Current position in the grid.
+ $x = 0;
+ $y = 0;
+ // Extent of the grid.
+ $min_y = 0;
+ $max_y = 0;
+ $max_x = 0;
+ // For each node in the path.
+ foreach ($path as $n => $xref) {
+ if ($n % 2 === 1) {
+ switch ($relationships[$n]) {
+ case 'hus':
+ case 'wif':
+ case 'spo':
+ case 'bro':
+ case 'sis':
+ case 'sib':
+ $table[$x+1][$y] = '<div style="background:url(' . Theme::theme()->parameter('image-hline') . ') repeat-x center; width: 65px; text-align: center"><span style="background: #fff;">' . get_relationship_name_from_path($relationships[$n], Individual::getInstance($path[$n - 1]), Individual::getInstance($path[$n + 1])) . $horizontal_arrow . '</span></div>';
+ $x += 2;
break;
case 'son':
- case 'daughter':
- case 'child':
- $arrow_img = $up_arrow;
- $line = Theme::theme()->parameter('image-vline');
- $liney += $Dbheight;
- $linex += $Dbwidth / 2;
- $lh = 54;
- $lw = 3;
- $lh = $ys;
- $linex = $xoffset + $Dbwidth / 2;
- // put the box up or down ?
- $yoffset -= $asc * ($Dbheight + $lh);
- $rowNum -= $asc;
- if ($asc == -1) {
- $liney = $yoffset - $lh;
+ case 'dau':
+ case 'chi':
+ if ($n > 2 && preg_match('/fat|mot|par/', $relationships[$n-2])) {
+ $table[$x + 1][$y - 1] = '<div style="background:url(' . Theme::theme()->parameter('image-dline2') . '); width: 65px; height: 50px; padding-top: 15px;"><div style="text-align: center; background: #fff;">' . get_relationship_name_from_path($relationships[$n], Individual::getInstance($path[$n - 1]), Individual::getInstance($path[$n + 1])) . $down_arrow . '</div></div>';
+ $x += 2;
} else {
- $liney = $yoffset + $Dbheight;
+ $table[$x][$y - 1] = '<div style="background:url(' . Theme::theme()
+ ->parameter('image-vline') . ') repeat-y center; height: 50px; padding-top: 15px;"><div style="text-align: center; background: #fff;">' . get_relationship_name_from_path($relationships[$n], Individual::getInstance($path[$n - 1]), Individual::getInstance($path[$n + 1])) . $down_arrow . '</div></div>';
}
- // need to draw a joining line ?
- if ($previous == 'parent' && ($change_count++ % 2) == 0) {
- $joinh = 3;
- $joinw = $xs / 2 + 2;
- $xoffset += $Dbwidth + $xs;
- $colNum++;
- //$rowNum is inherited from the box immediately to the left
- $linex = $xoffset - $xs / 2;
- if ($asc == 1) {
- $liney = $yoffset + $Dbheight;
- } else {
- $liney = $yoffset - ($lh + $Dbyspacing);
- }
- $joinx = $xoffset - $xs;
- $joiny = $liney - 2 + ($asc + 1) / 2 * $lh;
- echo '<div id="joina', $index, '" style="position:absolute; ', I18N::direction() === 'ltr' ? 'left' : 'right', ':', $joinx + $Dbxspacing, 'px; top:', $joiny + $Dbyspacing, 'px;" align="center"><img src="', Theme::theme()->parameter('image-hline'), '" align="left" width="', $joinw, '" height="', $joinh, '" alt=""></div>';
- $joinw = $xs / 2 + 2;
- $joinx = $joinx + $xs / 2;
- $joiny = $joiny - $asc * $lh;
- echo '<div id="joinb', $index, '" style="position:absolute; ', I18N::direction() === 'ltr' ? 'left' : 'right', ':', $joinx + $Dbxspacing, 'px; top:', $joiny + $Dbyspacing, 'px;" align="center"><img src="', Theme::theme()->parameter('image-hline'), '" align="left" width="', $joinw, '" height="', $joinh, '" alt=""></div>';
+ $y -= 2;
+ break;
+ case 'fat':
+ case 'mot':
+ case 'par':
+ if ($n > 2 && preg_match('/son|dau|chi/', $relationships[$n-2])) {
+ $table[$x + 1][$y + 1] = '<div style="background:url(' . Theme::theme()->parameter('image-dline') . '); width: 65px; height: 65px; padding-top: 15px;"><div style="text-align: center; background: #fff;">' . get_relationship_name_from_path($relationships[$n], Individual::getInstance($path[$n - 1]), Individual::getInstance($path[$n + 1])) . $up_arrow . '</div></div>';
+ $x += 2;
} else {
- $change_count = '';
+ $table[$x][$y + 1] = '<div style="background:url(' . Theme::theme()
+ ->parameter('image-vline') . ') repeat-y center; height: 50px; padding-top: 15px;"><div style="text-align: center; background: #fff;">' . get_relationship_name_from_path($relationships[$n], Individual::getInstance($path[$n - 1]), Individual::getInstance($path[$n + 1])) . $up_arrow . '</div></div>';
}
- $previous = 'child';
+ $y += 2;
break;
}
- if ($yoffset > $maxyoffset) {
- $maxyoffset = $yoffset;
- }
- $plinex = $linex;
- $pxoffset = $xoffset;
-
- // Adjust all box positions for proper placement with respect to other page elements
- $pyoffset = $yoffset - 2;
-
- if ($index > 0) {
- if (I18N::direction() === 'rtl' && $line !== Theme::theme()->parameter('image-hline')) {
- echo '<div id="line', $index, '" style="background:none; position:absolute; right:', $plinex + $Dbxspacing, 'px; top:', $liney + $Dbyspacing, 'px; width:', $lw + $lh * 2, 'px;" align="right">';
- echo '<img src="', $line, '" align="right" width="', $lw, '" height="', $lh, '" alt="">';
- echo '<br>';
- echo I18N::translate($node['relations'][$index]);
- echo '<i class="', $arrow_img, '"></i>';
- } else {
- echo '<div id="line', $index, '" style="background:none; position:absolute; ', I18N::direction() === 'ltr' ? 'left' : 'right', ':', $plinex + $Dbxspacing, 'px; top:', $liney + $Dbyspacing, 'px; width:', $lw + $lh * 2, 'px;" align="', $lh == 3 ? 'center' : 'left', '"><img src="', $line, '" align="left" width="', $lw, '" height="', $lh, '" alt="">';
- echo '<br>';
- echo '<i class="', $arrow_img, '"></i>';
- if ($lh == 3) {
- echo '<br>'; // note: $lh==3 means horiz arrow
- }
- echo I18N::translate($node['relations'][$index]);
- }
- echo '</div>';
+ $max_x = max($max_x, $x);
+ $min_y = min($min_y, $y);
+ $max_y = max($max_y, $y);
+ } else {
+ $individual = Individual::getInstance($xref);
+ ob_start();
+ print_pedigree_person($individual);
+ $table[$x][$y] = ob_get_clean();
+ }
+ }
+ echo '<table style="border-collapse: collapse; margin: 20px 50px;">';
+ for ($y = $max_y; $y >= $min_y; --$y) {
+ echo '<tr>';
+ for ($x = 0; $x <= $max_x; ++$x) {
+ echo '<td style="padding: 0;">';
+ if (isset($table[$x][$y])) {
+ echo $table[$x][$y];
}
-
- // Determine the z-index for this box
- $zIndex = 200 - ($colNum * $depth + $rowNum);
-
- echo '<div style="position:absolute; ', I18N::direction() === 'ltr' ? 'left' : 'right', ':', $pxoffset, 'px; top:', $pyoffset, 'px; width:', $Dbwidth, 'px; height:', $Dbheight, 'px; z-index:', $zIndex, ';">';
- print_pedigree_person($person);
- echo '</div>';
+ echo '</td>';
}
+ echo '</tr>';
}
- echo '</div>'; // close#relationship_chart
+ echo '</table>';
}
-}
-echo '</div>'; // close #relationshippage
-// The contents of <div id="relationship_chart"> use relative positions.
-// Need to expand the div to include the children, or we'll overlap the footer.
-// $maxyoffset is the top edge of the lowest box.
-$controller->addInlineJavascript('
- relationship_chart_div = document.getElementById("relationship_chart");
- if (relationship_chart_div) {
- relationship_chart_div.style.height = "'.($maxyoffset + $Dbheight + 20) . 'px";
- relationship_chart_div.style.width = "100%";
- }'
-);
+ if (!$num_paths) {
+ echo '<p>', I18N::translate('No link between the two individuals could be found.'), '</p>';
+ }
+} \ No newline at end of file
diff --git a/themes/clouds/css-1.7.0/style.css b/themes/clouds/css-1.7.0/style.css
index ce32745a0e..2e1c9e840a 100644
--- a/themes/clouds/css-1.7.0/style.css
+++ b/themes/clouds/css-1.7.0/style.css
@@ -3502,15 +3502,6 @@ table.table-census-assistant th {
position: absolute;
}
-/*-- Relationship ---- */
-#relationship-page h3 {
- margin: 20px 0 0 20px;
-}
-
-#relationship_chart {
- margin: 0 6px;
-}
-
/*-- timeline --*/
#timeline_chart {
position: relative;
diff --git a/themes/colors/css-1.7.0/style.css b/themes/colors/css-1.7.0/style.css
index 3971774919..55409446fa 100644
--- a/themes/colors/css-1.7.0/style.css
+++ b/themes/colors/css-1.7.0/style.css
@@ -3498,15 +3498,6 @@ table.table-census-assistant th {
position: absolute;
}
-/*-- Relationship ---- */
-#relationship-page h3 {
- margin: 20px 0 0 20px;
-}
-
-#relationship_chart {
- margin: 0 6px;
-}
-
/*-- timeline --*/
#timeline_chart {
position: relative;
diff --git a/themes/fab/css-1.7.0/style.css b/themes/fab/css-1.7.0/style.css
index 05da61b6d4..45a042d997 100644
--- a/themes/fab/css-1.7.0/style.css
+++ b/themes/fab/css-1.7.0/style.css
@@ -3426,15 +3426,6 @@ table.table-census-assistant th {
position: absolute;
}
-/*-- Relationship ---- */
-#relationship-page h3 {
- margin: 20px 0 0 20px;
-}
-
-#relationship_chart {
- margin: 0 6px;
-}
-
/*-- timeline --*/
#timeline_chart {
position: relative;
diff --git a/themes/minimal/css-1.7.0/style.css b/themes/minimal/css-1.7.0/style.css
index 7674231a5c..36596e5d47 100644
--- a/themes/minimal/css-1.7.0/style.css
+++ b/themes/minimal/css-1.7.0/style.css
@@ -3431,15 +3431,6 @@ table.table-census-assistant th {
position: absolute;
}
-/*-- Relationship ---- */
-#relationship-page h3 {
- margin: 20px 0 0 20px;
-}
-
-#relationship_chart {
- margin: 0 6px;
-}
-
/*-- timeline --*/
#timeline_chart {
position: relative;
diff --git a/themes/webtrees/css-1.7.0/style.css b/themes/webtrees/css-1.7.0/style.css
index bfa3469869..5718d6f465 100644
--- a/themes/webtrees/css-1.7.0/style.css
+++ b/themes/webtrees/css-1.7.0/style.css
@@ -3385,15 +3385,6 @@ table.table-census-assistant th {
position: absolute;
}
-/*-- Relationship ---- */
-#relationship-page h3 {
- margin: 20px 0 0 20px;
-}
-
-#relationship_chart {
- margin: 0 6px;
-}
-
/*-- timeline --*/
#timeline_chart {
position: relative;
diff --git a/themes/xenea/css-1.7.0/style.css b/themes/xenea/css-1.7.0/style.css
index 1eb5d99335..83e7aef15e 100644
--- a/themes/xenea/css-1.7.0/style.css
+++ b/themes/xenea/css-1.7.0/style.css
@@ -3396,15 +3396,6 @@ table.table-census-assistant th {
position: absolute;
}
-/*-- Relationship ---- */
-#relationship-page h3 {
- margin: 20px 0 0 20px;
-}
-
-#relationship_chart {
- margin: 0 20px;
-}
-
/*-- timeline --*/
#timeline_chart {
position: relative;
diff --git a/vendor/autoload.php b/vendor/autoload.php
index 48f332e5fe..8910960f3c 100644
--- a/vendor/autoload.php
+++ b/vendor/autoload.php
@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
-return ComposerAutoloaderInit207af526399948cc5e2e445108bc9b12::getLoader();
+return ComposerAutoloaderInitbc7ef3c4f9a7f3ece04ccd8101dc6f40::getLoader();
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index e8a8fb4bd3..2024efdb3c 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -7,6 +7,7 @@ $baseDir = dirname($vendorDir);
return array(
'Datamatrix' => $vendorDir . '/tecnick.com/tcpdf/include/barcodes/datamatrix.php',
+ 'Fisharebest\\Algorithm\\Dijkstra' => $vendorDir . '/fisharebest/algorithm/src/Dijkstra.php',
'Fisharebest\\ExtCalendar\\AbstractCalendar' => $vendorDir . '/fisharebest/ext-calendar/src/AbstractCalendar.php',
'Fisharebest\\ExtCalendar\\ArabicCalendar' => $vendorDir . '/fisharebest/ext-calendar/src/ArabicCalendar.php',
'Fisharebest\\ExtCalendar\\CalendarInterface' => $vendorDir . '/fisharebest/ext-calendar/src/CalendarInterface.php',
@@ -83,6 +84,7 @@ return array(
'Fisharebest\\Webtrees\\PageController' => $baseDir . '/app/Controller/PageController.php',
'Fisharebest\\Webtrees\\PedigreeController' => $baseDir . '/app/Controller/PedigreeController.php',
'Fisharebest\\Webtrees\\Place' => $baseDir . '/app/Place.php',
+ 'Fisharebest\\Webtrees\\RelationshipController' => $baseDir . '/app/Controller/RelationshipController.php',
'Fisharebest\\Webtrees\\ReportBase' => $baseDir . '/app/Report/ReportBase.php',
'Fisharebest\\Webtrees\\ReportBaseCell' => $baseDir . '/app/Report/ReportBaseCell.php',
'Fisharebest\\Webtrees\\ReportBaseElement' => $baseDir . '/app/Report/ReportBaseElement.php',
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index 1280ac6abc..991a431092 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -9,4 +9,5 @@ return array(
'Rhumsaa\\Uuid\\' => array($vendorDir . '/rhumsaa/uuid/src'),
'Fisharebest\\Webtrees\\' => array($baseDir . '/app', $baseDir . '/modules_v3'),
'Fisharebest\\ExtCalendar\\' => array($vendorDir . '/fisharebest/ext-calendar/src'),
+ 'Fisharebest\\Algorithm\\' => array($vendorDir . '/fisharebest/algorithm/src'),
);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index 42795084cc..6c352fe4b9 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderInit207af526399948cc5e2e445108bc9b12
+class ComposerAutoloaderInitbc7ef3c4f9a7f3ece04ccd8101dc6f40
{
private static $loader;
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit207af526399948cc5e2e445108bc9b12
return self::$loader;
}
- spl_autoload_register(array('ComposerAutoloaderInit207af526399948cc5e2e445108bc9b12', 'loadClassLoader'), true, true);
+ spl_autoload_register(array('ComposerAutoloaderInitbc7ef3c4f9a7f3ece04ccd8101dc6f40', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
- spl_autoload_unregister(array('ComposerAutoloaderInit207af526399948cc5e2e445108bc9b12', 'loadClassLoader'));
+ spl_autoload_unregister(array('ComposerAutoloaderInitbc7ef3c4f9a7f3ece04ccd8101dc6f40', 'loadClassLoader'));
$includePaths = require __DIR__ . '/include_paths.php';
array_push($includePaths, get_include_path());
@@ -46,14 +46,14 @@ class ComposerAutoloaderInit207af526399948cc5e2e445108bc9b12
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
- composerRequire207af526399948cc5e2e445108bc9b12($file);
+ composerRequirebc7ef3c4f9a7f3ece04ccd8101dc6f40($file);
}
return $loader;
}
}
-function composerRequire207af526399948cc5e2e445108bc9b12($file)
+function composerRequirebc7ef3c4f9a7f3ece04ccd8101dc6f40($file)
{
require $file;
}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 283c14fae2..91834c7d34 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -486,5 +486,53 @@
"ZF1",
"framework"
]
+ },
+ {
+ "name": "fisharebest/algorithm",
+ "version": "1.0.0",
+ "version_normalized": "1.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fisharebest/algorithm.git",
+ "reference": "ddfbf31e7b1ef49f227409a41261770ed3fb467b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fisharebest/algorithm/zipball/ddfbf31e7b1ef49f227409a41261770ed3fb467b",
+ "reference": "ddfbf31e7b1ef49f227409a41261770ed3fb467b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "satooshi/php-coveralls": "*"
+ },
+ "time": "2015-02-15 21:38:53",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Fisharebest\\Algorithm\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-3.0+"
+ ],
+ "authors": [
+ {
+ "name": "Greg Roach",
+ "email": "greg@subaqua.co.uk",
+ "role": "Developer"
+ }
+ ],
+ "description": "Implementation of standard algorithms in PHP.",
+ "homepage": "https://github.com/fisharebest/algorithm",
+ "keywords": [
+ "Algorithm",
+ "dijkstra"
+ ]
}
]