/** * Format age of parents in HTML * * @param Individual $person child * @param Date $birth_date * * @return string HTML */ public static function formatParentsAges(Individual $person, Date $birth_date) { $html = ''; $families = $person->getChildFamilies(); // Multiple sets of parents (e.g. adoption) cause complications, so ignore. if ($birth_date->isOK() && count($families) == 1) { $family = current($families); foreach ($family->getSpouses() as $parent) { if ($parent->getBirthDate()->isOK()) { $sex = $parent->getSexImage(); $age = Date::getAge($parent->getBirthDate(), $birth_date, 2); $deatdate = $parent->getDeathDate(); switch ($parent->getSex()) { case 'F': // Highlight mothers who die in childbirth or shortly afterwards if ($deatdate->isOK() && $deatdate->maximumJulianDay() < $birth_date->minimumJulianDay() + 90) { $html .= ' <span title="' . GedcomTag::getLabel('_DEAT_PARE', $parent) . '" class="parentdeath">' . $sex . $age . '</span>'; } else { $html .= ' <span title="' . I18N::translate('Mother’s age') . '">' . $sex . $age . '</span>'; } break; case 'M': // Highlight fathers who die before the birth if ($deatdate->isOK() && $deatdate->maximumJulianDay() < $birth_date->minimumJulianDay()) { $html .= ' <span title="' . GedcomTag::getLabel('_DEAT_PARE', $parent) . '" class="parentdeath">' . $sex . $age . '</span>'; } else { $html .= ' <span title="' . I18N::translate('Father’s age') . '">' . $sex . $age . '</span>'; } break; default: $html .= ' <span title="' . I18N::translate('Parent’s age') . '">' . $sex . $age . '</span>'; break; } } } if ($html) { $html = '<span class="age">' . $html . '</span>'; } } return $html; }
/** * Perform the search */ private function advancedSearch() { global $WT_TREE; $this->myindilist = array(); $fct = count($this->fields); if (!array_filter($this->values)) { return; } // Dynamic SQL query, plus bind variables $sql = 'SELECT DISTINCT ind.i_id AS xref, ind.i_gedcom AS gedcom FROM `##individuals` ind'; $bind = array(); // Join the following tables $father_name = false; $mother_name = false; $spouse_family = false; $indi_name = false; $indi_date = false; $fam_date = false; $indi_plac = false; $fam_plac = false; foreach ($this->fields as $n => $field) { if ($this->values[$n]) { if (substr($field, 0, 14) == 'FAMC:HUSB:NAME') { $father_name = true; } elseif (substr($field, 0, 14) == 'FAMC:WIFE:NAME') { $mother_name = true; } elseif (substr($field, 0, 4) == 'NAME') { $indi_name = true; } elseif (strpos($field, ':DATE') !== false) { if (substr($field, 0, 4) == 'FAMS') { $fam_date = true; $spouse_family = true; } else { $indi_date = true; } } elseif (strpos($field, ':PLAC') !== false) { if (substr($field, 0, 4) == 'FAMS') { $fam_plac = true; $spouse_family = true; } else { $indi_plac = true; } } elseif ($field == 'FAMS:NOTE') { $spouse_family = true; } } } if ($father_name || $mother_name) { $sql .= " JOIN `##link` l_1 ON (l_1.l_file=ind.i_file AND l_1.l_from=ind.i_id AND l_1.l_type='FAMC')"; } if ($father_name) { $sql .= " JOIN `##link` l_2 ON (l_2.l_file=ind.i_file AND l_2.l_from=l_1.l_to AND l_2.l_type='HUSB')"; $sql .= " JOIN `##name` f_n ON (f_n.n_file=ind.i_file AND f_n.n_id =l_2.l_to)"; } if ($mother_name) { $sql .= " JOIN `##link` l_3 ON (l_3.l_file=ind.i_file AND l_3.l_from=l_1.l_to AND l_3.l_type='WIFE')"; $sql .= " JOIN `##name` m_n ON (m_n.n_file=ind.i_file AND m_n.n_id =l_3.l_to)"; } if ($spouse_family) { $sql .= " JOIN `##link` l_4 ON (l_4.l_file=ind.i_file AND l_4.l_from=ind.i_id AND l_4.l_type='FAMS')"; $sql .= " JOIN `##families` fam ON (fam.f_file=ind.i_file AND fam.f_id =l_4.l_to)"; } if ($indi_name) { $sql .= " JOIN `##name` i_n ON (i_n.n_file=ind.i_file AND i_n.n_id=ind.i_id)"; } if ($indi_date) { $sql .= " JOIN `##dates` i_d ON (i_d.d_file=ind.i_file AND i_d.d_gid=ind.i_id)"; } if ($fam_date) { $sql .= " JOIN `##dates` f_d ON (f_d.d_file=ind.i_file AND f_d.d_gid=fam.f_id)"; } if ($indi_plac) { $sql .= " JOIN `##placelinks` i_pl ON (i_pl.pl_file=ind.i_file AND i_pl.pl_gid =ind.i_id)"; $sql .= " JOIN (" . "SELECT CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) AS place, p1.p_id AS id, p1.p_file AS file" . " FROM `##places` AS p1" . " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id=p2.p_id)" . " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id=p3.p_id)" . " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id=p4.p_id)" . " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id=p5.p_id)" . " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id=p6.p_id)" . " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id=p7.p_id)" . " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id=p8.p_id)" . " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id=p9.p_id)" . ") AS i_p ON (i_p.file =ind.i_file AND i_pl.pl_p_id= i_p.id)"; } if ($fam_plac) { $sql .= " JOIN `##placelinks` f_pl ON (f_pl.pl_file=ind.i_file AND f_pl.pl_gid =fam.f_id)"; $sql .= " JOIN (" . "SELECT CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) AS place, p1.p_id AS id, p1.p_file AS file" . " FROM `##places` AS p1" . " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id=p2.p_id)" . " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id=p3.p_id)" . " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id=p4.p_id)" . " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id=p5.p_id)" . " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id=p6.p_id)" . " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id=p7.p_id)" . " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id=p8.p_id)" . " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id=p9.p_id)" . ") AS f_p ON (f_p.file =ind.i_file AND f_pl.pl_p_id= f_p.id)"; } // Add the where clause $sql .= " WHERE ind.i_file=?"; $bind[] = $WT_TREE->getTreeId(); for ($i = 0; $i < $fct; $i++) { $field = $this->fields[$i]; $value = $this->values[$i]; if ($value === '') { continue; } $parts = preg_split("/:/", $field . '::::'); if ($parts[0] == 'NAME') { // NAME:* switch ($parts[1]) { case 'GIVN': switch ($parts[2]) { case 'EXACT': $sql .= " AND i_n.n_givn=?"; $bind[] = $value; break; case 'BEGINS': $sql .= " AND i_n.n_givn LIKE CONCAT(?, '%')"; $bind[] = $value; break; case 'CONTAINS': $sql .= " AND i_n.n_givn LIKE CONCAT('%', ?, '%')"; $bind[] = $value; break; case 'SDX_STD': $sdx = Soundex::russell($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "i_n.n_soundex_givn_std LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= ' AND (' . implode(' OR ', $sdx) . ')'; } else { // No phonetic content? Use a substring match $sql .= " AND i_n.n_givn LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } break; case 'SDX': // SDX uses DM by default. // SDX uses DM by default. case 'SDX_DM': $sdx = Soundex::daitchMokotoff($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "i_n.n_soundex_givn_dm LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= ' AND (' . implode(' OR ', $sdx) . ')'; } else { // No phonetic content? Use a substring match $sql .= " AND i_n.n_givn LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } break; } break; case 'SURN': switch ($parts[2]) { case 'EXACT': $sql .= " AND i_n.n_surname=?"; $bind[] = $value; break; case 'BEGINS': $sql .= " AND i_n.n_surname LIKE CONCAT(?, '%')"; $bind[] = $value; break; case 'CONTAINS': $sql .= " AND i_n.n_surname LIKE CONCAT('%', ?, '%')"; $bind[] = $value; break; case 'SDX_STD': $sdx = Soundex::russell($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "i_n.n_soundex_surn_std LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= " AND (" . implode(' OR ', $sdx) . ")"; } else { // No phonetic content? Use a substring match $sql .= " AND i_n.n_surn LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } break; case 'SDX': // SDX uses DM by default. // SDX uses DM by default. case 'SDX_DM': $sdx = Soundex::daitchMokotoff($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "i_n.n_soundex_surn_dm LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= " AND (" . implode(' OR ', $sdx) . ")"; break; } else { // No phonetic content? Use a substring match $sql .= " AND i_n.n_surn LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } } break; case 'NICK': case '_MARNM': case '_HEB': case '_AKA': $sql .= " AND i_n.n_type=? AND i_n.n_full LIKE CONCAT('%', ?, '%')"; $bind[] = $parts[1]; $bind[] = $value; break; } } elseif ($parts[1] == 'DATE') { // *:DATE $date = new Date($value); if ($date->isOK()) { $jd1 = $date->minimumJulianDay(); $jd2 = $date->maximumJulianDay(); if (!empty($this->plusminus[$i])) { $adjd = $this->plusminus[$i] * 365; $jd1 -= $adjd; $jd2 += $adjd; } $sql .= " AND i_d.d_fact=? AND i_d.d_julianday1>=? AND i_d.d_julianday2<=?"; $bind[] = $parts[0]; $bind[] = $jd1; $bind[] = $jd2; } } elseif ($parts[0] == 'FAMS' && $parts[2] == 'DATE') { // FAMS:*:DATE $date = new Date($value); if ($date->isOK()) { $jd1 = $date->minimumJulianDay(); $jd2 = $date->maximumJulianDay(); if (!empty($this->plusminus[$i])) { $adjd = $this->plusminus[$i] * 365; $jd1 -= $adjd; $jd2 += $adjd; } $sql .= " AND f_d.d_fact=? AND f_d.d_julianday1>=? AND f_d.d_julianday2<=?"; $bind[] = $parts[1]; $bind[] = $jd1; $bind[] = $jd2; } } elseif ($parts[1] == 'PLAC') { // *:PLAC // SQL can only link a place to a person/family, not to an event. $sql .= " AND i_p.place LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } elseif ($parts[0] == 'FAMS' && $parts[2] == 'PLAC') { // FAMS:*:PLAC // SQL can only link a place to a person/family, not to an event. $sql .= " AND f_p.place LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } elseif ($parts[0] == 'FAMC' && $parts[2] == 'NAME') { $table = $parts[1] == 'HUSB' ? 'f_n' : 'm_n'; // NAME:* switch ($parts[3]) { case 'GIVN': switch ($parts[4]) { case 'EXACT': $sql .= " AND {$table}.n_givn=?"; $bind[] = $value; break; case 'BEGINS': $sql .= " AND {$table}.n_givn LIKE CONCAT(?, '%')"; $bind[] = $value; break; case 'CONTAINS': $sql .= " AND {$table}.n_givn LIKE CONCAT('%', ?, '%')"; $bind[] = $value; break; case 'SDX_STD': $sdx = Soundex::russell($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "{$table}.n_soundex_givn_std LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= ' AND (' . implode(' OR ', $sdx) . ')'; } else { // No phonetic content? Use a substring match $sql .= " AND {$table}.n_givn = LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } break; case 'SDX': // SDX uses DM by default. // SDX uses DM by default. case 'SDX_DM': $sdx = Soundex::daitchMokotoff($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "{$table}.n_soundex_givn_dm LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= ' AND (' . implode(' OR ', $sdx) . ')'; break; } else { // No phonetic content? Use a substring match $sql .= " AND {$table}.n_givn = LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } } break; case 'SURN': switch ($parts[4]) { case 'EXACT': $sql .= " AND {$table}.n_surname=?"; $bind[] = $value; break; case 'BEGINS': $sql .= " AND {$table}.n_surname LIKE CONCAT(?, '%')"; $bind[] = $value; break; case 'CONTAINS': $sql .= " AND {$table}.n_surname LIKE CONCAT('%', ?, '%')"; $bind[] = $value; break; case 'SDX_STD': $sdx = Soundex::russell($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "{$table}.n_soundex_surn_std LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= ' AND (' . implode(' OR ', $sdx) . ')'; } else { // No phonetic content? Use a substring match $sql .= " AND {$table}.n_surn = LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } break; case 'SDX': // SDX uses DM by default. // SDX uses DM by default. case 'SDX_DM': $sdx = Soundex::daitchMokotoff($value); if ($sdx !== null) { $sdx = explode(':', $sdx); foreach ($sdx as $k => $v) { $sdx[$k] = "{$table}.n_soundex_surn_dm LIKE CONCAT('%', ?, '%')"; $bind[] = $v; } $sql .= ' AND (' . implode(' OR ', $sdx) . ')'; } else { // No phonetic content? Use a substring match $sql .= " AND {$table}.n_surn = LIKE CONCAT('%', ?, '%')"; $bind[] = $value; } break; } break; } } elseif ($parts[0] == 'FAMS') { // e.g. searches for occupation, religion, note, etc. $sql .= " AND fam.f_gedcom REGEXP CONCAT('\n[0-9] ', ?, '(.*\n[0-9] CONT)* [^\n]*', ?)"; $bind[] = $parts[1]; $bind[] = $value; } else { // e.g. searches for occupation, religion, note, etc. $sql .= " AND ind.i_gedcom REGEXP CONCAT('\n[0-9] ', ?, '(.*\n[0-9] CONT)* [^\n]*', ?)"; $bind[] = $parts[0]; $bind[] = $value; } } $rows = Database::prepare($sql)->execute($bind)->fetchAll(); foreach ($rows as $row) { $person = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom); // Check for XXXX:PLAC fields, which were only partially matched by SQL foreach ($this->fields as $n => $field) { if ($this->values[$n] && preg_match('/^(' . WT_REGEX_TAG . '):PLAC$/', $field, $match)) { if (!preg_match('/\\n1 ' . $match[1] . '(\\n[2-9].*)*\\n2 PLAC .*' . preg_quote($this->values[$n], '/') . '/i', $person->getGedcom())) { continue 2; } } } $this->myindilist[] = $person; } }
/** * Calculate whether this individual is living or dead. * If not known to be dead, then assume living. * * @return bool */ public function isDead() { $MAX_ALIVE_AGE = $this->tree->getPreference('MAX_ALIVE_AGE'); // "1 DEAT Y" or "1 DEAT/2 DATE" or "1 DEAT/2 PLAC" if (preg_match('/\\n1 (?:' . WT_EVENTS_DEAT . ')(?: Y|(?:\\n[2-9].+)*\\n2 (DATE|PLAC) )/', $this->gedcom)) { return true; } // If any event occured more than $MAX_ALIVE_AGE years ago, then assume the individual is dead if (preg_match_all('/\\n2 DATE (.+)/', $this->gedcom, $date_matches)) { foreach ($date_matches[1] as $date_match) { $date = new Date($date_match); if ($date->isOK() && $date->maximumJulianDay() <= WT_CLIENT_JD - 365 * $MAX_ALIVE_AGE) { return true; } } // The individual has one or more dated events. All are less than $MAX_ALIVE_AGE years ago. // If one of these is a birth, the individual must be alive. if (preg_match('/\\n1 BIRT(?:\\n[2-9].+)*\\n2 DATE /', $this->gedcom)) { return false; } } // If we found no conclusive dates then check the dates of close relatives. // Check parents (birth and adopted) foreach ($this->getChildFamilies(Auth::PRIV_HIDE) as $family) { foreach ($family->getSpouses(Auth::PRIV_HIDE) as $parent) { // Assume parents are no more than 45 years older than their children preg_match_all('/\\n2 DATE (.+)/', $parent->gedcom, $date_matches); foreach ($date_matches[1] as $date_match) { $date = new Date($date_match); if ($date->isOK() && $date->maximumJulianDay() <= WT_CLIENT_JD - 365 * ($MAX_ALIVE_AGE + 45)) { return true; } } } } // Check spouses foreach ($this->getSpouseFamilies(Auth::PRIV_HIDE) as $family) { preg_match_all('/\\n2 DATE (.+)/', $family->gedcom, $date_matches); foreach ($date_matches[1] as $date_match) { $date = new Date($date_match); // Assume marriage occurs after age of 10 if ($date->isOK() && $date->maximumJulianDay() <= WT_CLIENT_JD - 365 * ($MAX_ALIVE_AGE - 10)) { return true; } } // Check spouse dates $spouse = $family->getSpouse($this); if ($spouse) { preg_match_all('/\\n2 DATE (.+)/', $spouse->gedcom, $date_matches); foreach ($date_matches[1] as $date_match) { $date = new Date($date_match); // Assume max age difference between spouses of 40 years if ($date->isOK() && $date->maximumJulianDay() <= WT_CLIENT_JD - 365 * ($MAX_ALIVE_AGE + 40)) { return true; } } } // Check child dates foreach ($family->getChildren(Auth::PRIV_HIDE) as $child) { preg_match_all('/\\n2 DATE (.+)/', $child->gedcom, $date_matches); // Assume children born after age of 15 foreach ($date_matches[1] as $date_match) { $date = new Date($date_match); if ($date->isOK() && $date->maximumJulianDay() <= WT_CLIENT_JD - 365 * ($MAX_ALIVE_AGE - 15)) { return true; } } // Check grandchildren foreach ($child->getSpouseFamilies(Auth::PRIV_HIDE) as $child_family) { foreach ($child_family->getChildren(Auth::PRIV_HIDE) as $grandchild) { preg_match_all('/\\n2 DATE (.+)/', $grandchild->gedcom, $date_matches); // Assume grandchildren born after age of 30 foreach ($date_matches[1] as $date_match) { $date = new Date($date_match); if ($date->isOK() && $date->maximumJulianDay() <= WT_CLIENT_JD - 365 * ($MAX_ALIVE_AGE - 30)) { return true; } } } } } } return false; }
/** * Print a family group. * * @param Family $family * @param string $type * @param string $label */ private function printFamily(Family $family, $type, $label) { global $controller; if ($family->getTree()->getPreference('SHOW_PRIVATE_RELATIONSHIPS')) { $access_level = Auth::PRIV_HIDE; } else { $access_level = Auth::accessLevel($family->getTree()); } ?> <table> <tr> <td> <i class="icon-cfamily"></i> </td> <td> <span class="subheaders"> <?php echo $label; ?> </span> <a class="noprint" href="<?php echo $family->getHtmlUrl(); ?> "> - <?php echo I18N::translate('View this family'); ?> </a> </td> </tr> </table> <table class="facts_table"> <?php ///// HUSB ///// $found = false; foreach ($family->getFacts('HUSB', false, $access_level) as $fact) { $found |= !$fact->isPendingDeletion(); $person = $fact->getTarget(); if ($person instanceof Individual) { if ($fact->isPendingAddition()) { $class = 'facts_label new'; } elseif ($fact->isPendingDeletion()) { $class = 'facts_label old'; } else { $class = 'facts_label'; } ?> <tr> <td class="<?php echo $class; ?> "> <?php echo Functions::getCloseRelationshipName($controller->record, $person); ?> </td> <td class="<?php echo $controller->getPersonStyle($person); ?> "> <?php echo Theme::theme()->individualBoxLarge($person); ?> </td> </tr> <?php } } if (!$found && $family->canEdit()) { ?> <tr> <td class="facts_label"></td> <td class="facts_value"><a href="#" onclick="return add_spouse_to_family('<?php echo $family->getXref(); ?> ', 'HUSB');"><?php echo I18N::translate('Add a husband to this family'); ?> </a></td> </tr> <?php } ///// WIFE ///// $found = false; foreach ($family->getFacts('WIFE', false, $access_level) as $fact) { $person = $fact->getTarget(); if ($person instanceof Individual) { $found |= !$fact->isPendingDeletion(); if ($fact->isPendingAddition()) { $class = 'facts_label new'; } elseif ($fact->isPendingDeletion()) { $class = 'facts_label old'; } else { $class = 'facts_label'; } ?> <tr> <td class="<?php echo $class; ?> "> <?php echo Functions::getCloseRelationshipName($controller->record, $person); ?> </td> <td class="<?php echo $controller->getPersonStyle($person); ?> "> <?php echo Theme::theme()->individualBoxLarge($person); ?> </td> </tr> <?php } } if (!$found && $family->canEdit()) { ?> <tr> <td class="facts_label"></td> <td class="facts_value"><a href="#" onclick="return add_spouse_to_family('<?php echo $family->getXref(); ?> ', 'WIFE');"><?php echo I18N::translate('Add a wife to this family'); ?> </a></td> </tr> <?php } ///// MARR ///// $found = false; $prev = new Date(''); foreach ($family->getFacts(WT_EVENTS_MARR . '|' . WT_EVENTS_DIV, true) as $fact) { $found |= !$fact->isPendingDeletion(); if ($fact->isPendingAddition()) { $class = ' new'; } elseif ($fact->isPendingDeletion()) { $class = ' old'; } else { $class = ''; } ?> <tr> <td class="facts_label"> </td> <td class="facts_value<?php echo $class; ?> "> <?php echo GedcomTag::getLabelValue($fact->getTag(), $fact->getDate()->display() . ' — ' . $fact->getPlace()->getFullName()); ?> </td> </tr> <?php if (!$prev->isOK() && $fact->getDate()->isOK()) { $prev = $fact->getDate(); } } if (!$found && $family->canShow() && $family->canEdit()) { // Add a new marriage ?> <tr> <td class="facts_label"> </td> <td class="facts_value"> <a href="#" onclick="return add_new_record('<?php echo $family->getXref(); ?> ', 'MARR');"> <?php echo I18N::translate('Add marriage details'); ?> </a> </td> </tr> <?php } ///// CHIL ///// $child_number = 0; foreach ($family->getFacts('CHIL', false, $access_level) as $fact) { $person = $fact->getTarget(); if ($person instanceof Individual) { if ($fact->isPendingAddition()) { $child_number++; $class = 'facts_label new'; } elseif ($fact->isPendingDeletion()) { $class = 'facts_label old'; } else { $child_number++; $class = 'facts_label'; } $next = new Date(''); foreach ($person->getFacts(WT_EVENTS_BIRT, true) as $bfact) { if ($bfact->getDate()->isOK()) { $next = $bfact->getDate(); break; } } ?> <tr> <td class="<?php echo $class; ?> "> <?php echo self::ageDifference($prev, $next, $child_number); ?> <?php echo Functions::getCloseRelationshipName($controller->record, $person); ?> </td> <td class="<?php echo $controller->getPersonStyle($person); ?> "> <?php echo Theme::theme()->individualBoxLarge($person); ?> </td> </tr> <?php $prev = $next; } } // Re-order children / add a new child if ($family->canEdit()) { if ($type == 'FAMS') { $add_child_text = I18N::translate('Add a son or daughter'); } else { $add_child_text = I18N::translate('Add a brother or sister'); } ?> <tr class="noprint"> <td class="facts_label"> <?php if (count($family->getChildren()) > 1) { ?> <a href="#" onclick="reorder_children('<?php echo $family->getXref(); ?> ');tabswitch(5);"><i class="icon-media-shuffle"></i> <?php echo I18N::translate('Re-order children'); ?> </a> <?php } ?> </td> <td class="facts_value"> <a href="#" onclick="return add_child_to_family('<?php echo $family->getXref(); ?> ');"><?php echo $add_child_text; ?> </a> <span style='white-space:nowrap;'> <a href="#" class="icon-sex_m_15x15" onclick="return add_child_to_family('<?php echo $family->getXref(); ?> ','M');"></a> <a href="#" class="icon-sex_f_15x15" onclick="return add_child_to_family('<?php echo $family->getXref(); ?> ','F');"></a> </span> </td> </tr> <?php } echo '</table>'; return; }