1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2018 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16namespace Fisharebest\Webtrees\Module; 17 18use Fisharebest\Webtrees\Family; 19use Fisharebest\Webtrees\Functions\Functions; 20use Fisharebest\Webtrees\I18N; 21use Fisharebest\Webtrees\Individual; 22use Fisharebest\Webtrees\Menu; 23 24/** 25 * Class FamilyNavigatorModule 26 */ 27class FamilyNavigatorModule extends AbstractModule implements ModuleSidebarInterface { 28 const TTL = "<div class='flyout2'>%s</div>"; 29 const LNK = "<div class='flyout3' data-href='%s'>%s</div>"; 30 const MSG = "<div class='flyout4'>(%s)</div>"; // class flyout4 not used in standard themes 31 32 /** {@inheritdoc} */ 33 public function getTitle() { 34 return /* I18N: Name of a module/sidebar */ I18N::translate('Family navigator'); 35 } 36 37 /** {@inheritdoc} */ 38 public function getDescription() { 39 return /* I18N: Description of the “Family navigator” module */ I18N::translate('A sidebar showing an individual’s close families and relatives.'); 40 } 41 42 /** {@inheritdoc} */ 43 public function defaultSidebarOrder() { 44 return 20; 45 } 46 47 /** {@inheritdoc} */ 48 public function hasSidebarContent(Individual $individual) { 49 return true; 50 } 51 52 /** {@inheritdoc} */ 53 public function getSidebarAjaxContent() { 54 return ''; 55 } 56 57 /** 58 * Load this sidebar synchronously. 59 * 60 * @param Individual $individual 61 * 62 * @return string 63 */ 64 public function getSidebarContent(Individual $individual) { 65 global $controller; 66 67 $controller->addInlineJavascript(' 68 $("#sb_family_nav_content") 69 .on("click", ".flyout a", function() { 70 return false; 71 }) 72 .on("click", ".flyout3", function() { 73 window.location.href = $(this).data("href"); 74 return false; 75 }); 76 '); 77 78 ob_start(); 79 80 ?> 81 <div id="sb_family_nav_content"> 82 <div class="nav_content"> 83 84 <?php 85 //-- parent families ------------------------------------------------------------- 86 foreach ($individual->getChildFamilies() as $family) { 87 $this->drawFamily($individual, $family, $individual->getChildFamilyLabel($family)); 88 } 89 //-- step parents ---------------------------------------------------------------- 90 foreach ($individual->getChildStepFamilies() as $family) { 91 $this->drawFamily($individual, $family, $individual->getStepFamilyLabel($family)); 92 } 93 //-- spouse and children -------------------------------------------------- 94 foreach ($individual->getSpouseFamilies() as $family) { 95 $this->drawFamily($individual, $family, $individual->getSpouseFamilyLabel($family)); 96 } 97 //-- step children ---------------------------------------------------------------- 98 foreach ($individual->getSpouseStepFamilies() as $family) { 99 $this->drawFamily($individual, $family, $family->getFullName()); 100 } 101 ?> 102 </div> 103 </div> 104 <?php 105 106 return ob_get_clean(); 107 } 108 109 /** 110 * Format a family. 111 * 112 * @param Family $family 113 * @param string $title 114 */ 115 private function drawFamily(Individual $individual, Family $family, $title) { 116 global $controller; 117 118 ?> 119 <table class="table table-sm wt-facts-table"> 120 <caption class="text-center"> 121 <a class="famnav_title" href="<?= e($family->url()) ?>"> 122 <?= $title ?> 123 </a> 124 </caption> 125 <tbody> 126 <?php 127 foreach ($family->getSpouses() as $spouse) { 128 $icon = $individual === $spouse ? '<i class="icon-selected"></i>' : ''; 129 $menu = new Menu($icon . Functions::getCloseRelationshipName($individual, $spouse)); 130 $menu->addSubmenu(new Menu($this->getParents($spouse))); 131 ?> 132 <tr class="text-center wt-parent wt-gender-<?= $spouse->getSex() ?>"> 133 <th scope="row"> 134 <ul class="nav"> 135 <?= $menu->bootstrap4() ?> 136 </ul> 137 </th> 138 <td> 139 <?php if ($spouse->canShow()): ?> 140 <a class="famnav_link" href="<?= e($spouse->url()) ?>"> 141 <?= $spouse->getFullName() ?> 142 </a> 143 <div class="small"> 144 <?= $spouse->getLifeSpan() ?> 145 </div> 146 <?php else: ?> 147 <?= $spouse->getFullName() ?> 148 <?php endif ?> 149 </td> 150 </tr> 151 <?php 152 } 153 154 foreach ($family->getChildren() as $child) { 155 $icon = $individual === $child ? '<i class="icon-selected"></i>' : ''; 156 $menu = new Menu($icon . Functions::getCloseRelationshipName($individual, $child)); 157 $menu->addSubmenu(new Menu($this->getFamily($child))); 158 ?> 159 <tr class="text-center wt-child wt-gender-<?= $child->getSex() ?>"> 160 <th scope="row"> 161 <ul class="nav"> 162 <?= $menu->bootstrap4() ?> 163 </ul> 164 </th> 165 <td> 166 <?php if ($child->canShow()): ?> 167 <a class="famnav_link" href="<?= e($child->url()) ?>"> 168 <?= $child->getFullName() ?> 169 </a> 170 <div class="small"> 171 <?= $child->getLifeSpan() ?> 172 </div> 173 <?php else: ?> 174 <?= $child->getFullName() ?> 175 <?php endif ?> 176 </td> 177 </tr> 178 <?php 179 } 180 ?> 181 </tbody> 182 </table> 183 <?php 184 } 185 186 /** 187 * Format an individual. 188 * 189 * @param $person 190 * @param bool $showUnknown 191 * 192 * @return string 193 */ 194 private function getHTML($person, $showUnknown = false) { 195 if ($person instanceof Individual) { 196 return sprintf(self::LNK, e($person->url()), $person->getFullName()); 197 } elseif ($showUnknown) { 198 return sprintf(self::MSG, I18N::translate('unknown')); 199 } else { 200 return ''; 201 } 202 } 203 204 /** 205 * Forat the parents of an individual. 206 * 207 * @param Individual $person 208 * 209 * @return string 210 */ 211 private function getParents(Individual $person) { 212 $father = null; 213 $mother = null; 214 $html = sprintf(self::TTL, I18N::translate('Parents')); 215 $family = $person->getPrimaryChildFamily(); 216 if ($person->canShowName() && $family !== null) { 217 $father = $family->getHusband(); 218 $mother = $family->getWife(); 219 $html .= $this->getHTML($father) . 220 $this->getHTML($mother); 221 222 // Can only have a step parent if one & only one parent found at this point 223 if ($father instanceof Individual xor $mother instanceof Individual) { 224 $stepParents = ''; 225 foreach ($person->getChildStepFamilies() as $family) { 226 if (!$father instanceof Individual) { 227 $stepParents .= $this->getHTML($family->getHusband()); 228 } else { 229 $stepParents .= $this->getHTML($family->getWife()); 230 } 231 } 232 if ($stepParents) { 233 $relationship = $father instanceof Individual ? 234 I18N::translateContext('father’s wife', 'step-mother') : I18N::translateContext('mother’s husband', 'step-father'); 235 $html .= sprintf(self::TTL, $relationship) . $stepParents; 236 } 237 } 238 } 239 if (!($father instanceof Individual || $mother instanceof Individual)) { 240 $html .= sprintf(self::MSG, I18N::translateContext('unknown family', 'unknown')); 241 } 242 243 return $html; 244 } 245 246 /** 247 * Format a family. 248 * 249 * @param Individual $person 250 * 251 * @return string 252 */ 253 private function getFamily(Individual $person) { 254 $html = ''; 255 if ($person->canShowName()) { 256 foreach ($person->getSpouseFamilies() as $family) { 257 $spouse = $family->getSpouse($person); 258 $html .= $this->getHTML($spouse, true); 259 $children = $family->getChildren(); 260 if (count($children) > 0) { 261 $html .= "<ul class='clist'>"; 262 foreach ($children as $child) { 263 $html .= '<li>' . $this->getHTML($child) . '</li>'; 264 } 265 $html .= '</ul>'; 266 } 267 } 268 } 269 if (!$html) { 270 $html = sprintf(self::MSG, I18N::translate('none')); 271 } 272 273 return sprintf(self::TTL, I18N::translate('Family')) . $html; 274 } 275} 276