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() { 49 return true; 50 } 51 52 /** {@inheritdoc} */ 53 public function getSidebarAjaxContent() { 54 return ''; 55 } 56 57 /** 58 * Load this sidebar synchronously. 59 * 60 * @return string 61 */ 62 public function getSidebarContent() { 63 global $controller; 64 65 $controller->addInlineJavascript(' 66 $("#sb_family_nav_content") 67 .on("click", ".flyout a", function() { 68 return false; 69 }) 70 .on("click", ".flyout3", function() { 71 window.location.href = $(this).data("href"); 72 return false; 73 }); 74 '); 75 76 ob_start(); 77 78 ?> 79 <div id="sb_family_nav_content"> 80 <div class="nav_content"> 81 82 <?php 83 //-- parent families ------------------------------------------------------------- 84 foreach ($controller->record->getChildFamilies() as $family) { 85 $this->drawFamily($family, $controller->record->getChildFamilyLabel($family)); 86 } 87 //-- step parents ---------------------------------------------------------------- 88 foreach ($controller->record->getChildStepFamilies() as $family) { 89 $this->drawFamily($family, $controller->record->getStepFamilyLabel($family)); 90 } 91 //-- spouse and children -------------------------------------------------- 92 foreach ($controller->record->getSpouseFamilies() as $family) { 93 $this->drawFamily($family, $controller->getSpouseFamilyLabel($family, $controller->record)); 94 } 95 //-- step children ---------------------------------------------------------------- 96 foreach ($controller->record->getSpouseStepFamilies() as $family) { 97 $this->drawFamily($family, $family->getFullName()); 98 } 99 ?> 100 </div> 101 </div> 102 <?php 103 104 return ob_get_clean(); 105 } 106 107 /** 108 * Format a family. 109 * 110 * @param Family $family 111 * @param string $title 112 */ 113 private function drawFamily(Family $family, $title) { 114 global $controller; 115 116 ?> 117 <table class="table table-sm wt-facts-table"> 118 <caption class="text-center"> 119 <a class="famnav_title" href="<?= e($family->url()) ?>"> 120 <?= $title ?> 121 </a> 122 </caption> 123 <tbody> 124 <?php 125 foreach ($family->getSpouses() as $spouse) { 126 $icon = $controller->record === $spouse ? '<i class="icon-selected"></i>' : ''; 127 $menu = new Menu($icon . Functions::getCloseRelationshipName($controller->record, $spouse)); 128 $menu->addSubmenu(new Menu($this->getParents($spouse))); 129 ?> 130 <tr class="text-center wt-parent wt-gender-<?= $spouse->getSex() ?>"> 131 <th scope="row"> 132 <ul class="nav"> 133 <?= $menu->bootstrap4() ?> 134 </ul> 135 </th> 136 <td> 137 <?php if ($spouse->canShow()): ?> 138 <a class="famnav_link" href="<?= e($spouse->url()) ?>"> 139 <?= $spouse->getFullName() ?> 140 </a> 141 <div class="small"> 142 <?= $spouse->getLifeSpan() ?> 143 </div> 144 <?php else: ?> 145 <?= $spouse->getFullName() ?> 146 <?php endif ?> 147 </td> 148 </tr> 149 <?php 150 } 151 152 foreach ($family->getChildren() as $child) { 153 $icon = $controller->record === $child ? '<i class="icon-selected"></i>' : ''; 154 $menu = new Menu($icon . Functions::getCloseRelationshipName($controller->record, $child)); 155 $menu->addSubmenu(new Menu($this->getFamily($child))); 156 ?> 157 <tr class="text-center wt-child wt-gender-<?= $child->getSex() ?>"> 158 <th scope="row"> 159 <ul class="nav"> 160 <?= $menu->bootstrap4() ?> 161 </ul> 162 </th> 163 <td> 164 <?php if ($child->canShow()): ?> 165 <a class="famnav_link" href="<?= e($child->url()) ?>"> 166 <?= $child->getFullName() ?> 167 </a> 168 <div class="small"> 169 <?= $child->getLifeSpan() ?> 170 </div> 171 <?php else: ?> 172 <?= $child->getFullName() ?> 173 <?php endif ?> 174 </td> 175 </tr> 176 <?php 177 } 178 ?> 179 </tbody> 180 </table> 181 <?php 182 } 183 184 /** 185 * Format an individual. 186 * 187 * @param $person 188 * @param bool $showUnknown 189 * 190 * @return string 191 */ 192 private function getHTML($person, $showUnknown = false) { 193 if ($person instanceof Individual) { 194 return sprintf(self::LNK, e($person->url()), $person->getFullName()); 195 } elseif ($showUnknown) { 196 return sprintf(self::MSG, I18N::translate('unknown')); 197 } else { 198 return ''; 199 } 200 } 201 202 /** 203 * Forat the parents of an individual. 204 * 205 * @param Individual $person 206 * 207 * @return string 208 */ 209 private function getParents(Individual $person) { 210 $father = null; 211 $mother = null; 212 $html = sprintf(self::TTL, I18N::translate('Parents')); 213 $family = $person->getPrimaryChildFamily(); 214 if ($person->canShowName() && $family !== null) { 215 $father = $family->getHusband(); 216 $mother = $family->getWife(); 217 $html .= $this->getHTML($father) . 218 $this->getHTML($mother); 219 220 // Can only have a step parent if one & only one parent found at this point 221 if ($father instanceof Individual xor $mother instanceof Individual) { 222 $stepParents = ''; 223 foreach ($person->getChildStepFamilies() as $family) { 224 if (!$father instanceof Individual) { 225 $stepParents .= $this->getHTML($family->getHusband()); 226 } else { 227 $stepParents .= $this->getHTML($family->getWife()); 228 } 229 } 230 if ($stepParents) { 231 $relationship = $father instanceof Individual ? 232 I18N::translateContext('father’s wife', 'step-mother') : I18N::translateContext('mother’s husband', 'step-father'); 233 $html .= sprintf(self::TTL, $relationship) . $stepParents; 234 } 235 } 236 } 237 if (!($father instanceof Individual || $mother instanceof Individual)) { 238 $html .= sprintf(self::MSG, I18N::translateContext('unknown family', 'unknown')); 239 } 240 241 return $html; 242 } 243 244 /** 245 * Format a family. 246 * 247 * @param Individual $person 248 * 249 * @return string 250 */ 251 private function getFamily(Individual $person) { 252 $html = ''; 253 if ($person->canShowName()) { 254 foreach ($person->getSpouseFamilies() as $family) { 255 $spouse = $family->getSpouse($person); 256 $html .= $this->getHTML($spouse, true); 257 $children = $family->getChildren(); 258 if (count($children) > 0) { 259 $html .= "<ul class='clist'>"; 260 foreach ($children as $child) { 261 $html .= '<li>' . $this->getHTML($child) . '</li>'; 262 } 263 $html .= '</ul>'; 264 } 265 } 266 } 267 if (!$html) { 268 $html = sprintf(self::MSG, I18N::translate('none')); 269 } 270 271 return sprintf(self::TTL, I18N::translate('Family')) . $html; 272 } 273} 274