diff options
Diffstat (limited to 'modules_v3/tree/class_treeview.php')
| -rw-r--r-- | modules_v3/tree/class_treeview.php | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/modules_v3/tree/class_treeview.php b/modules_v3/tree/class_treeview.php new file mode 100644 index 0000000000..7a43196194 --- /dev/null +++ b/modules_v3/tree/class_treeview.php @@ -0,0 +1,458 @@ +<?php +// Class file for the tree navigator +// +// Note : NEVER use person or family id as ids because a same person could +// appear more than once in the tree. +// +// webtrees: Web based Family History software +// Copyright (C) 2011 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 2 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// $Id$ + +if (!defined('WT_WEBTREES')) { + header('HTTP/1.0 403 Forbidden'); + exit; +} + +require_once 'includes/functions/functions_charts.php'; + +class TreeView { + var $name; + var $allPartners; + + /** + * Treeview Constructor + * @param string $name the name of the TreeView object's instance + */ + function __construct($name='tree') { + $this->name = $name; + + // Read if all partners must be shown or not + $allPartners = safe_GET('allPartners'); + // if allPartners not specified in url, we try to read the cookie + if ($allPartners == '') { + if (isset($_COOKIE['allPartners'])) + $allPartners = $_COOKIE['allPartners']; + else + $allPartners = 'true'; // That is now the default value + } + $allPartners = ($allPartners == 'true' ? true : false); + $this->allPartners = $allPartners; + } + + /** + * Draw the viewport which creates the draggable/zoomable framework + * Size is set by the container, as the viewport can scale itself automatically + * @param string $rootPersonId the id of the root person + * @param int $generations number of generations to draw + */ + public function drawViewport($rootPersonId, $generations, $style) { + global $GEDCOM, $WT_IMAGES; + + $rootPersonId = check_rootid($rootPersonId); + $rootPerson = WT_Person::getInstance($rootPersonId); + if (is_null($rootPerson)) + $rootPerson = new WT_Person(''); + + if (WT_SCRIPT_NAME == 'individual.php') + $path = 'individual.php?pid='.$rootPerson->getXref().'&ged='.$GEDCOM.'&allPartners='.($this->allPartners ? "false" : "true").'#tree'; + else + $path = 'module.php?mod=tree&mod_action=treeview&rootid='.$rootPerson->getXref().'&allPartners='.($this->allPartners ? "false" : "true"); + $r = '<a name="tv_content"></a><div id="'.$this->name.'_out" dir="ltr" class="tv_out">'; + + // Read styles (20 maxi) in a hidden list + $sd = WT_MODULES_DIR.'tree/css/styles/'; + $rs = '<ul id="tvStylesSubmenu">'; + $cs = '<img src="'.$sd.'default/button.gif" alt="d" onclick="'.$this->name.'Handler.style(\''.$sd.'\', \'default\', this);" title="'.WT_I18N::translate('Style').'" />'; + $rs .= '<li class="tv_button'.($style == '' || $style=='default' ? ' tvPressed' : '').'">'.$cs.'</li>'; + $nbStyles = 1; + if (@is_dir($sd) && @is_readable($sd) && ($d=@opendir($sd))) { + while (($s = readdir($d)) !== false && ($nbStyles < 20)) { + if ($s[0] == '.' || $s=='default' || !is_dir($sd.$s)) + continue; + $sHTML = '<img src="'.$sd.$s.'/button.gif" alt="'.$s[0].'" onclick="'.$this->name.'Handler.style(\''.$sd.'\', \''.$s.'\', this);" title="'.WT_I18N::translate('Style').'" />'; + if ($s == $style) { + $cs = $sHTML; + $pressedState = ' tvPressed'; + } + else + $pressedState =''; + $rs .= '<li class="tv_button'.$pressedState.'">'.$sHTML.'</li>'; + $nbStyles++; + } + } + $rs .= '</ul>'; + + // Add the toolbar + $r.= + '<div id="tv_tools"><ul>'. + // TODO: can we change the toolbar's orientation automatically, when it is dragged to a vertical/horizontal edge? + '<li id="tvToolsHandler" title="'.WT_I18N::translate('Move the toolbar').'"></li>'. + '<li id="tvbZoomIn" class="tv_button"><img src="'.$WT_IMAGES['zoomin'].'" alt="'.WT_I18N::translate('Zoom in').'" title="'.WT_I18N::translate('Zoom in').'" /></li>'. + '<li id="tvbZoomOut" class="tv_button"><img src="'.$WT_IMAGES['zoomout'].'" alt="'.WT_I18N::translate('Zoom out').'" title="'.WT_I18N::translate('Zoom out').'" /></li>'. + '<li id="tvbNoZoom" class="tv_button"><img src="'.WT_MODULES_DIR.'tree/images/zoom0.png" alt="'.WT_I18N::translate('Reset').'" title="'.WT_I18N::translate('Reset').'" /></li>'. + '<li id="tvbLeft" class="tv_button"><img src="'.$WT_IMAGES['ldarrow'].'" alt="'.WT_I18N::translate('Align left').'" title="'.WT_I18N::translate('Align left').'" /></li>'. + '<li id="tvbCenter" class="tv_button"><img src="'.$WT_IMAGES['center'].'" alt="'./* I18N: verb/action */ WT_I18N::translate('Center').'" title="'.WT_I18N::translate('Center').'" /></li>'. + '<li id="tvbRight" class="tv_button"><img src="'.$WT_IMAGES['rdarrow'].'" alt="'.WT_I18N::translate('Align right').'" title="'.WT_I18N::translate('Align right').'" /></li>'. + '<li id="tvbDates" class="tv_button tvPressed"><img src="'.WT_MODULES_DIR.'tree/images/dates.png" alt="'.WT_I18N::translate('Show year of birth and death').'" title="'.WT_I18N::translate('Show year of birth and death').'" /></li>'. + '<li id="tvbCompact" class="tv_button"><img src="'.WT_MODULES_DIR.'tree/images/compact.png" alt="'.WT_I18N::translate('Use compact layout').'" title="'.WT_I18N::translate('Use compact layout').'" /></li>'. + // TODO: this is temporarily disabled (as it sends a flood of AJAX requests?) + //'<li id="tvbOpen" class="tv_button"><img src="'.$WT_IMAGES["media"].'" alt="o" title="'.WT_I18N::translate('Show all details').'" /></li>'. + //'<li id="tvbClose" class="tv_button"><img src="'.$WT_IMAGES["fambook"].'" alt="f" title="'.WT_I18N::translate('Hide all details').'" /></li>'. + // If the position/order of the style button moves, update TreeViewHandler() in treeview.js + '<li id="tvStyleButton" class="tv_button">'.$cs.'</li>'. + '<li id="tvbPrint" class="tv_button"><img src="'.WT_MODULES_DIR.'tree/images/print.png" alt="p" title="'./* I18N: verb/action */ WT_I18N::translate('Print').'" /></li>'. + '<li class="tv_button'.($this->allPartners ? ' tvPressed' : '').'"><a href="'.$path.'"><img src="'.$WT_IMAGES["sfamily"].'" alt="'.WT_I18N::translate('Show all spouses and ancestors').'" title="'.WT_I18N::translate('Show all spouses and ancestors').'" /></a></li>'; + if (safe_GET('mod_action') != 'treeview') { + $r.='<li class="tv_button"><a href="module.php?mod=tree&mod_action=treeview&rootid='.$rootPerson->getXref().'#tv_content" title="'. /* I18N: Button label - view this chart in full-screen mode */ WT_I18N::translate('Full screen').'"><img src="'.$WT_IMAGES["fscreen"].'" alt="full screen" /></a></li>'; + } + // Help, and hidden loading image + $r.='<li class="tv_button">'.help_link("TV_MODULE", 'tree').'</li> + <li class="tv_button" id="'.$this->name.'_loading"><img src="images/loading.gif" alt="Loading..." /></li> +</ul>'.$rs; + $r.='</div><div id="'.$this->name.'_in" class="tv_in">'; + $parent = null; + $r.=$this->drawPerson($rootPerson, $generations, 0, $parent, '', true); + $r.='</div></div>'; // Close the tv_in and the tv_out div + $r.='<script type="text/javascript">var '.$this->name.'Handler = new TreeViewHandler("'.$this->name.'", '.($this->allPartners ? 'true' : 'false').', '.$nbStyles.');</script>'; + return $r; + } + + /** + * Return a JSON structure to a JSON request + * @param string $list list of JSON requests + */ + public function getPersons($list) { + $list = explode(';', $list); + $r = array(); + foreach($list as $jsonRequest) { + $firstLetter = substr($jsonRequest, 0, 1); + $jsonRequest = substr($jsonRequest, 1); + switch($firstLetter) { + case 'c': + $fidlist = explode(',', $jsonRequest); + $flist = array(); + foreach($fidlist as $fid) + $flist[] = WT_Family::getInstance($fid); + $r[] = $this->drawChildren($flist, 1, true); + break; + case 'p': + $params = explode('@', $jsonRequest); + $fid = $params[0]; + $order = $params[1]; + $f = WT_Family::getInstance($fid); + $p = $f->getHusband(); + $r[] = $this->drawPerson($f->getHusband(), 0, 1, $f, $order); + break; + } + } + return json_encode($r); + } + + /** + * Get the details for a person and their life partner(s) + * @param string $pid the person id to return the details for + */ + public function getDetails($pid) { + + $person = WT_Person::getInstance($pid); + + $r = $this->getPersonDetails($person, $person, null); + foreach ($person->getSpouseFamilies() as $family) { + if (!empty($family)) { + $partner = $family->getSpouse($person); + if (!empty($partner)) + $r .= $this->getPersonDetails($person, $partner, $family); + } + } + return $r; + } + + /** + * Get full resolution medias + * @param string $medias the list of medias to return + */ + public function getMedias($medias) { + $medias = explode(';', $medias); + $nb = count($medias); + $r = array(); + for ($i=0; $i<$nb; $i++) { + $mid = $medias[$i]; + $m = WT_Media::getInstance($mid); + $r[] = $m->getServerFilename(); + } + return json_encode($r); + } + + + /** + * Return the details for a person + * @param Person $person the person to return the details for + */ + private function getPersonDetails($personGroup, $person, $family) { + global $WT_IMAGES; + + $r = '<div class="tv'.$person->getSex().' tv_person_expanded">'; + $r .= $this->getThumbnail($personGroup, $person); + $r .= '<a class="tv_link" href="'.$person->getHtmlUrl().'">'.$person->getFullName().'</a> <a href="module.php?mod=tree&mod_action=treeview&allPartners='.($this->allPartners ? 'true' : 'false').'&rootid='.$person->getXref().'" title="'.WT_I18N::translate('Interactive tree of %s', htmlspecialchars(strip_tags($person->getFullName()))).'"><img src="'.$WT_IMAGES['tree'].'" class="tv_link tv_treelink" /></a>'; + $r .= '<br /><b>'.WT_Gedcom_Tag::getAbbreviation('BIRT').'</b> '.$person->getBirthDate()->Display().' '.$person->getBirthPlace(); + if ($family) { + $r .= '<br /><b>'.WT_Gedcom_Tag::getAbbreviation('MARR').'</b> '.$family->getMarriageDate()->Display().' <a href="'.$family->getHtmlUrl().'"><img src="'.$WT_IMAGES['button_family'].'" class="tv_link tv_treelink" title="'.htmlspecialchars(strip_tags($family->getFullName())).'" /></a>'.$family->getMarriagePlace(); + } + if ($person->isDead()) + $r .= '<br /><b>'.WT_Gedcom_Tag::getAbbreviation('DEAT').'</b> '.$person->getDeathDate()->Display().' '.$person->getDeathPlace(); + $r.= '</div>'; + return $r; + } + + /** + * Draw the children for some families + * @param Array $familyList array of families to draw the children for + * @param int $gen number of generations to draw + * @param boolean $ajax setted to true for an ajax call + */ + private function drawChildren($familyList, $gen=1, $ajax=false) { + $r =''; + $flWithChildren = array(); + $f2load = array(); + $tc = 0; + foreach($familyList as $f) { + if (empty($f)) + continue; + $nbcf = $f->getNumberOfChildren(); + if ($nbcf > 0) { + $flWithChildren[] = $f; + $f2load[] = $f->getXref(); + $tc += $nbcf; + } + } + if ($tc) { + $f2load = implode(',', $f2load); + if (!$ajax) + $r .= '<td align="right"'.($gen == 0 ? ' abbr="c'.$f2load.'"' : '').'>'; + $nbc = 0; + foreach($flWithChildren as $f) { + foreach ($f->getChildren() as $child) { + $nbc++; + if ($tc == 1) + $co = 'c'; // unique + elseif ($nbc == 1) + $co = 't'; // first + elseif($nbc == $tc) + $co = 'b'; //last + else + $co = 'h'; + $fam = null; + $r .= $this->drawPerson($child, $gen-1, -1, $fam, $co); + } + } + if (!$ajax) + $r .= '</td>'.$this->drawHorizontalLine(); + } + return $r; + } + + /** + * Draw a person in the tree + * @param Person $person The Person object to draw the box for + * @param int $gen The number of generations up or down to print + * @param int $state Whether we are going up or down the tree, -1 for descendents +1 for ancestors + * @param Family $pfamily + * @param string $order first (1), last(2), unique(0), or empty. Required for drawing lines between boxes + * + * Notes : "spouse" means explicitely married partners. Thus, the word "partner" + * (for "life partner") here fits much better than "spouse" or "mate" + * to translate properly the modern french meaning of "conjoint" + */ + private function drawPerson($person, $gen, $state=0, $pfamily, $order, $isRoot=false) { + global $TEXT_DIRECTION; + + if ($gen < 0 || empty($person)) + return; + + if (!empty($pfamily)) + $partner = $pfamily->getSpouse($person); + else { + $partner = $person->getCurrentSpouse(); + $fams = $person->getSpouseFamilies(); + $pfamily = end($fams); + } + + if ($isRoot) + $r = '<table id="tvTreeBorder" class="tv_tree"><tbody><tr><td id="tv_tree_topleft"></td><td id="tv_tree_top"><div>'. + WT_I18N::translate('Interactive tree of %s',$person->getFullName()). + '</div></td><td id="tv_tree_topright"></td></tr><tr><td id="tv_tree_left"></td><td>'; + else $r = ''; + /* height 1% : this hack enable the div auto-dimensionning in td for FF & Chrome */ + $r .= '<table class="tv_tree"'.($isRoot ? ' id="tv_tree"' : '').' style="height: 1%"><tbody><tr>'; + + if ($state<=0) { + // draw children + $r.=$this->drawChildren($person->getSpouseFamilies(), $gen); + } else { + // draw the parent's lines + $r .= $this->drawVerticalLine($order).$this->drawHorizontalLine(); + } + + /* draw the person. Do NOT add person or family id as an id, since a same person could appear more than once in the tree !!! */ + // Fixing the width for td to the box initial width when the person is the root person fix a rare bug that happen when a person without child and without known parents is the root person : an unwanted white rectangle appear at the right of the person's boxes, otherwise. + $r .= '<td'.($isRoot ? ' style="width:1px"' : '').'><div class="tv_box'.($isRoot ? ' rootPerson' : '').'" dir="'.$TEXT_DIRECTION.'" style="text-align: '.($TEXT_DIRECTION=="rtl" ? "right":"left").'; direction: '.$TEXT_DIRECTION.'" abbr="'.$person->getXref().'" onclick="'.$this->name.'Handler.expandBox(this, event);">'; + $r .= $this->drawPersonName($person); + $fop = Array(); // $fop is fathers of partners + if (!is_null($partner)) { + $sfams = $person->getSpouseFamilies(); + foreach ($sfams as $famid=>$family) { + $p = $family->getSpouse($person); + if (!empty($p)) { + if (($p->equals($partner)) || $this->allPartners) { + $pf = $p->getPrimaryChildFamily(); + if (!empty($pf)) + $fop[] = Array($pf->getHusband(), $pf); + $r .= $this->drawPersonName($p); + if (!$this->allPartners) + break; // we can stop here the foreach loop + } + } + } + } + $r .= '</div></td>'; + + $fatherFamily = $person->getPrimaryChildFamily(); + if (!empty($fatherFamily)) + $father = $fatherFamily->getHusband(); + if (!empty($father) || count($fop) || ($state < 0)) + $r .= $this->drawHorizontalLine(); + + /* draw the parents */ + if ($state >= 0 && (!empty($father) || count($fop))) { + $unique = (empty($father) || count($fop) == 0); + $r .= '<td align="left"><table class="tv_tree"><tbody>'; + if (!empty($father)) { + $u = ($unique ? 'c' : 't'); + $r .= '<tr><td '.($gen == 0 ? ' abbr="p'.$fatherFamily->getXref().'@'.$u.'"' : '').'>'; + $r .= $this->drawPerson($father, $gen-1, 1, $fatherFamily, $u); + $r .= '</td></tr>'; + } + if (count($fop)) { + $n = 0; + $nb = count($fop); + foreach($fop as $p) { + $n++; + $u = ($unique ? 'c' : ($n == $nb || empty($p[1]) || !$this->allPartners ? 'b' : 'h')); + $r .= '<tr><td '.($gen == 0 ? ' abbr="p'.$p[1]->getXref().'@'.$u.'"' : '').'>'.$this->drawPerson($p[0], $gen-1, 1, $p[1], $u).'</td></tr>'; + } + } + $r .= '</tbody></table></td>'; + } + if ($state < 0) { + $r .= $this->drawVerticalLine($order); + } + $r .= '</tr></tbody></table>'; + if ($isRoot) + $r .= '</td><td id="tv_tree_right"></td></tr><tr><td id="tv_tree_bottomleft"></td><td id="tv_tree_bottom"></td><td id="tv_tree_bottomright"></td></tr></tbody></table>'; + return $r; + } + + /** + * Draw a person name preceded by sex icon, with parents as tooltip + * @param WT_Person $p a person + */ + private function drawPersonName($p) { + if ($this->allPartners) { + $f = $p->getPrimaryChildFamily(); + if ($f) { + switch ($p->getSex()) { + case 'M': + $title=' title="'.htmlspecialchars(strip_tags(/* I18N: e.g. "Son of [father name & mother name]" */ WT_I18N::translate('Son of %s', $f->getFullName()))).'"'; + break; + case 'F': + $title=' title="'.htmlspecialchars(strip_tags(/* I18N: e.g. "Daughter of [father name & mother name]" */ WT_I18N::translate('Daughter of %s', $f->getFullName()))).'"'; + break; + case 'U': + $title=' title="'.htmlspecialchars(strip_tags(/* I18N: e.g. "Child of [father name & mother name]" */ WT_I18N::translate('Child of %s', $f->getFullName()))).'"'; + break; + } + } else { + $title=''; + } + } + else + $title = ''; + $sex = $p->getSex(); + switch($sex) { + case 'M': + $sexSymbol = WT_UTF8_MALE; + break; + case 'F': + $sexSymbol = WT_UTF8_FEMALE; + break; + default: + $sexSymbol = WT_UTF8_NO_SEX; + break; + } + $r = '<div class="tv'.$sex.'"'.$title.'><span class="dates">'.$p->getLifeSpan().'</span><a href="'.$p->getHtmlUrl().'"><span class="tvSexSymbol tv'.$sex.' tv_link">'.$sexSymbol.'</span></a> '.$p->getFullName().'</div>'; + return $r; + } + + /** + * Get the thumbnail image for the given person + * + * @param Person $person + * @return string + */ + private function getThumbnail($personGroup, $person) { + global $MULTI_MEDIA, $SHOW_HIGHLIGHT_IMAGES; + + $thumbnail=""; + if ($MULTI_MEDIA && $SHOW_HIGHLIGHT_IMAGES) { + $object=$person->findHighlightedMedia(); + $img_title=PrintReady(htmlspecialchars($person->getFullName())); + if (!empty($object)) { + $mediaobject=WT_Media::getInstance($object['mid']); + $thumbnail=$mediaobject->displayMedia(array('display_type'=>'treeview','img_title'=>$img_title,'clearbox'=>'tvlb'.$personGroup->getXref())); + } else { + $thumbnail=display_silhouette(array('sex'=>$person->getSex(),'display_type'=>'treeview','img_title'=>$img_title)); // may return '' + } + } + + return $thumbnail; + } + + /** + * Draw a vertical line + * @param string order $order A parameter that set how to draw this line + * with auto-redimensionning capabilities + * WARNING : some tricky hacks are required in CSS to ensure cross-browser compliance + * some browsers shows an image, which imply a size limit in height, + * and some other browsers (ex: firefox) shows a <div> tag, which have no size limit in height + * Therefore, Firefox is a good choice to print very big trees. + */ + private function drawVerticalLine($order) { + $r = '<td class="tv_vline tv_vline_'.$order.'"><div class="tv_vline tv_vline_'.$order.'"></div></td>'; + return $r; + } + + /** + * Draw an horizontal line + */ + private function drawHorizontalLine() { + $r = '<td class="tv_hline"><div class="tv_hline"></div></td>'; + return $r; + } + +} + |
