1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/webtrees_ynh.git synced 2024-09-03 18:26:37 +02:00
webtrees_ynh/sources/app/Controller/HourglassController.php

457 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Controller;
use Fisharebest\Webtrees\Filter;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Theme;
/**
* Controller for the hourglass chart
*/
class HourglassController extends ChartController {
/** @var int Whether to show spouse details. */
public $show_spouse;
/** @var int Number of ascendancy generations to show. */
public $generations;
/** @var int Number of descendancy generations that exist. */
public $dgenerations;
/** @var int Half height of personbox. */
public $bhalfheight;
/** @var string An arrow that points to the start of the line */
private $left_arrow;
/** @var string An arrow that points to the end of the line. */
private $right_arrow;
/** @var bool Can the Javascript be loaded by the controller. */
private $canLoadJS;
const LINK = "<a class='%s' href='%s' data-parms='%s-%s-%s'></a>";
const SWITCH_LINK = "<a href='hourglass.php?rootid=%s&amp;show_spouse=%s&amp;show_full=%s&amp;generations=%s' class='name1'>%s</a>";
/**
* Create the hourglass controller.
*
* @param string $rootid
* @param int $show_full
* @param bool $loadJS
*/
public function __construct($rootid = '', $show_full = 1, $loadJS = true) {
global $WT_TREE;
parent::__construct($show_full);
// Extract parameters from
$this->show_spouse = Filter::getInteger('show_spouse', 0, 1, 0);
$this->generations = Filter::getInteger('generations', 2, $WT_TREE->getPreference('MAX_DESCENDANCY_GENERATIONS'), 3);
$this->canLoadJS = $loadJS;
//-- flip the arrows for RTL languages
if (I18N::direction() === 'ltr') {
$this->left_arrow = 'icon-larrow';
$this->right_arrow = 'icon-rarrow';
} else {
$this->left_arrow = 'icon-rarrow';
$this->right_arrow = 'icon-larrow';
}
$this->bhalfheight = (int) ($this->getBoxDimensions()->height / 2);
//Checks how many generations of descendency is for the person for formatting purposes
$this->dgenerations = $this->maxDescendencyGenerations($this->root, 0);
if ($this->dgenerations < 1) {
$this->dgenerations = 1;
}
$this->setPageTitle(/* I18N: %s is an individuals name */ I18N::translate('Hourglass chart of %s', $this->root->getFullName()));
}
/**
* Prints pedigree of the person passed in. Which is the descendancy
*
* @param Individual $person ID of person to print the pedigree for
* @param int $count generation count, so it recursively calls itself
*/
public function printPersonPedigree(Individual $person, $count) {
if ($count >= $this->generations) {
return;
}
$genoffset = $this->generations; // handle pedigree n generations lines
//
//Prints empty table columns for children w/o parents up to the max generation
//This allows vertical line spacing to be consistent
//
if (count($person->getChildFamilies()) == 0) {
echo '<table><tr><td>' . $this->printEmptyBox() . '</td>';
echo '<td>';
// Recursively get the fathers family
$this->printPersonPedigree($person, $count + 1);
echo '</td></tr>';
echo '<tr><td>' . $this->printEmptyBox() . '</td>';
echo '<td>';
// Recursively get the mothers family
$this->printPersonPedigree($person, $count + 1);
echo '</td><td></tr></table>';
}
foreach ($person->getChildFamilies() as $family) {
echo '<table class="hourglassChart">';
echo '<tr>';
echo '<td style="vertical-align:bottom"><img class="line3 pvline" src="' . Theme::theme()->parameter('image-vline') . '" width="3"></td>';
echo '<td><img class="line4" src="' . Theme::theme()->parameter('image-hline') . '" width="7" height="3"></td>';
echo '<td>';
//-- print the father box
FunctionsPrint::printPedigreePerson($family->getHusband(), $this->showFull());
echo "</td>";
if ($family->getHusband()) {
$ARID = $family->getHusband()->getXref();
echo "<td id=\"td_" . $ARID . "\">";
//-- print an Ajax arrow on the last generation of the adult male
if ($count == $this->generations - 1 && $family->getHusband()->getChildFamilies()) {
printf(self::LINK, $this->right_arrow, $ARID, 'asc', $this->showFull(), $this->show_spouse);
}
//-- recursively get the fathers family
$this->printPersonPedigree($family->getHusband(), $count + 1);
echo "</td>";
} else {
echo '<td>';
if ($count < $genoffset - 1) {
echo '<table>';
for ($i = $count; $i < (pow(2, ($genoffset - 1) - $count) / 2) + 2; $i++) {
$this->printEmptyBox();
echo '</tr>';
$this->printEmptyBox();
echo '</tr>';
}
echo '</table>';
}
}
echo
'</tr><tr>',
"<td style='vertical-align:top'><img class='pvline' src='" . Theme::theme()->parameter('image-vline') . "' width='3' alt=''></td>",
'<td><img class="line4" src="' . Theme::theme()->parameter('image-hline') . '" width="7" height="3"></td>',
'<td>';
//-- print the mother box
FunctionsPrint::printPedigreePerson($family->getWife(), $this->showFull());
echo '</td>';
if ($family->getWife()) {
$ARID = $family->getWife()->getXref();
echo '<td id="td_' . $ARID . '">';
//-- print an ajax arrow on the last generation of the adult female
if ($count == $this->generations - 1 && $family->getWife()->getChildFamilies()) {
printf(self::LINK, $this->right_arrow, $ARID, 'asc', $this->showFull(), $this->show_spouse);
}
//-- recursively print the mothers family
$this->printPersonPedigree($family->getWife(), $count + 1);
echo '</td>';
}
echo '</tr></table>';
break;
}
}
/**
* Print empty box
*
* @return string
*/
private function printEmptyBox() {
return $this->showFull() ? Theme::theme()->individualBoxEmpty() : Theme::theme()->individualBoxSmallEmpty();
}
/**
* Prints descendency of passed in person
*
* @param Individual $person person to print descendency for
* @param int $count count of generations to print
* @param bool $showNav
*
* @return int
*/
public function printDescendency($person, $count, $showNav = true) {
global $lastGenSecondFam;
if ($count > $this->dgenerations) {
return 0;
}
$pid = $person->getXref();
$tablealign = 'right';
$otablealign = 'left';
if (I18N::direction() === 'rtl') {
$tablealign = 'left';
$otablealign = 'right';
}
//-- put a space between families on the last generation
if ($count == $this->dgenerations - 1) {
if (isset($lastGenSecondFam)) {
echo '<br>';
}
$lastGenSecondFam = true;
}
echo "<table id='table_$pid' class='hourglassChart' style='float:$tablealign'>";
echo '<tr>';
echo "<td style='text-align:$tablealign'>";
$numkids = 0;
$families = $person->getSpouseFamilies();
$famNum = 0;
$children = array();
if ($count < $this->dgenerations) {
// Put all of the children in a common array
foreach ($families as $family) {
$famNum++;
foreach ($family->getChildren() as $child) {
$children[] = $child;
}
}
$ct = count($children);
if ($ct > 0) {
echo "<table style='position: relative; top: auto; float: $tablealign;'>";
for ($i = 0; $i < $ct; $i++) {
$person2 = $children[$i];
$chil = $person2->getXref();
echo '<tr>';
echo '<td id="td_', $chil, '" class="', I18N::direction(), '" style="text-align:', $otablealign, '">';
$kids = $this->printDescendency($person2, $count + 1, $showNav);
$numkids += $kids;
echo '</td>';
// Print the lines
if ($ct > 1) {
if ($i == 0) {
// First child
echo "<td style='vertical-align:bottom'><img alt='' class='line1 tvertline' id='vline_$chil' src='" . Theme::theme()->parameter('image-vline') . "' width='3'></td>";
} elseif ($i == $ct - 1) {
// Last child
echo "<td style='vertical-align:top'><img alt='' class='bvertline' id='vline_$chil' src='" . Theme::theme()->parameter('image-vline') . "' width='3'></td>";
} else {
// Middle child
echo '<td style="background: url(\'' . Theme::theme()->parameter('image-vline') . '\');"><img src=\'' . Theme::theme()->parameter('image-spacer') . '\' width="3"></td>';
}
}
echo '</tr>';
}
echo '</table>';
}
echo '</td>';
echo '<td width="', $this->getBoxDimensions()->width, '">';
}
// Print the descendency expansion arrow
if ($count == $this->dgenerations) {
$numkids = 1;
$tbwidth = $this->getBoxDimensions()->width + 16;
for ($j = $count; $j < $this->dgenerations; $j++) {
echo "<div style='width: ", $tbwidth, "px;'><br></div></td><td style='width:", $this->getBoxDimensions()->width, "px'>";
}
$kcount = 0;
foreach ($families as $family) {
$kcount += $family->getNumberOfChildren();
}
if ($kcount == 0) {
echo "</td><td style='width:", $this->getBoxDimensions()->width, "px'>";
} else {
printf(self::LINK, $this->left_arrow, $pid, 'desc', $this->showFull(), $this->show_spouse);
//-- move the arrow up to line up with the correct box
if ($this->show_spouse) {
echo str_repeat('<br><br><br>', count($families));
}
echo "</td><td style='width:", $this->getBoxDimensions()->width, "px'>";
}
}
echo '<table id="table2_' . $pid . '"><tr><td>';
FunctionsPrint::printPedigreePerson($person, $this->showFull());
echo '</td><td><img class="line2" src="' . Theme::theme()->parameter('image-hline') . '" width="7" height="3">';
//----- Print the spouse
if ($this->show_spouse) {
foreach ($families as $family) {
echo "</td></tr><tr><td style='text-align:$otablealign'>";
FunctionsPrint::printPedigreePerson($family->getSpouse($person), $this->showFull());
$numkids++;
echo "</td><td></td>";
}
//-- add offset divs to make things line up better
if ($count == $this->dgenerations) {
echo "<tr><td colspan '2'><div style='height:", ($this->bhalfheight / 2), "px; width:", $this->getBoxDimensions()->width, "px;'><br></div>";
}
}
echo "</td></tr></table>";
// For the root person, print a down arrow that allows changing the root of tree
if ($showNav && $count == 1) {
if ($person->canShowName()) {
// -- print left arrow for decendants so that we can move down the tree
$famids = $person->getSpouseFamilies();
//-- make sure there is more than 1 child in the family with parents
$cfamids = $person->getChildFamilies();
$num = 0;
foreach ($cfamids as $family) {
$num += $family->getNumberOfChildren();
}
if ($num > 0) {
echo '<div class="center" id="childarrow" style="position:absolute; width:', $this->getBoxDimensions()->width, 'px;">';
echo '<a href="#" class="icon-darrow"></a>';
echo '<div id="childbox">';
echo '<table class="person_box"><tr><td>';
foreach ($famids as $family) {
echo "<span class='name1'>" . I18N::translate('Family') . "</span>";
$spouse = $family->getSpouse($person);
if ($spouse) {
printf(self::SWITCH_LINK, $spouse->getXref(), $this->show_spouse, $this->showFull(), $this->generations, $spouse->getFullName());
}
foreach ($family->getChildren() as $child) {
printf(self::SWITCH_LINK, $child->getXref(), $this->show_spouse, $this->showFull(), $this->generations, $child->getFullName());
}
}
//-- print the siblings
foreach ($cfamids as $family) {
if ($family->getHusband() || $family->getWife()) {
echo "<span class='name1'>" . I18N::translate('Parents') . "</span>";
$husb = $family->getHusband();
if ($husb) {
printf(self::SWITCH_LINK, $husb->getXref(), $this->show_spouse, $this->showFull(), $this->generations, $husb->getFullName());
}
$wife = $family->getWife();
if ($wife) {
printf(self::SWITCH_LINK, $wife->getXref(), $this->show_spouse, $this->showFull(), $this->generations, $wife->getFullName());
}
}
// filter out root person from children array so only siblings remain
$siblings = array_filter($family->getChildren(), function (Individual $item) use ($pid) {
return $item->getXref() != $pid;
});
$num = count($siblings);
if ($num) {
echo "<span class='name1'>";
echo $num > 1 ? I18N::translate('Siblings') : I18N::translate('Sibling');
echo "</span>";
foreach ($siblings as $child) {
printf(self::SWITCH_LINK, $child->getXref(), $this->show_spouse, $this->showFull(), $this->generations, $child->getFullName());
}
}
}
echo '</td></tr></table>';
echo '</div>';
echo '</div>';
}
}
}
echo '</td></tr></table>';
return $numkids;
}
/**
* Calculates number of generations a person has
*
* @param Individual $individual Start individual
* @param int $depth Pass in 0 and it calculates how far down descendency goes
*
* @return int Number of generations the descendency actually goes
*/
private function maxDescendencyGenerations(Individual $individual, $depth) {
if ($depth > $this->generations) {
return $depth;
}
$maxdc = $depth;
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getChildren() as $child) {
$dc = $this->maxDescendencyGenerations($child, $depth + 1);
if ($dc >= $this->generations) {
return $dc;
}
if ($dc > $maxdc) {
$maxdc = $dc;
}
}
}
$maxdc++;
if ($maxdc == 1) {
$maxdc++;
}
return $maxdc;
}
/**
* setup all of the javascript that is needed for the hourglass chart
*/
public function setupJavascript() {
$js = "
var WT_HOURGLASS_CHART = (function() {
function sizeLines() {
jQuery('.tvertline').each(function(i,e) {
var pid = e.id.split('_').pop();
e.style.height = Math.abs(jQuery('#table_' + pid)[0].offsetHeight - (jQuery('#table2_' + pid)[0].offsetTop + {$this->bhalfheight}+5)) + 'px';
});
jQuery('.bvertline').each(function(i,e) {
var pid = e.id.split('_').pop();
e.style.height = jQuery('#table_' + pid)[0].offsetTop + jQuery('#table2_' + pid)[0].offsetTop + {$this->bhalfheight}+5 + 'px';
});
jQuery('.pvline').each(function(i,e) {
var el = jQuery(e);
el.height(Math.floor(el.parent().height()/2));
});
}
jQuery('#childarrow').on('click', '.icon-darrow', function(e) {
e.preventDefault();
jQuery('#childbox').slideToggle('fast');
})
jQuery('.hourglassChart').on('click', '.icon-larrow, .icon-rarrow', function(e){
e.preventDefault();
e.stopPropagation();
var self = jQuery(this),
parms = self.data('parms').split('-'),
id = self.attr('href');
jQuery('#td_'+id).load('hourglass_ajax.php?rootid='+ id +'&generations=1&type='+parms[0]+'&show_full='+(parseInt(parms[1]) ? 1:0) +'&show_spouse='+(parseInt(parms[2]) ? 1:0), function(){
sizeLines();
});
});
sizeLines();
})();
";
if ($this->canLoadJS) {
$this->addInlineJavascript($js);
} else {
return $js;
}
}
}