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\Database; 19use Fisharebest\Webtrees\Family; 20use Fisharebest\Webtrees\FontAwesome; 21use Fisharebest\Webtrees\I18N; 22use Fisharebest\Webtrees\Individual; 23use Fisharebest\Webtrees\Tree; 24use Symfony\Component\HttpFoundation\Request; 25use Symfony\Component\HttpFoundation\Response; 26 27/** 28 * Class DescendancyModule 29 */ 30class DescendancyModule extends AbstractModule implements ModuleSidebarInterface { 31 /** {@inheritdoc} */ 32 public function getTitle() { 33 return /* I18N: Name of a module/sidebar */ 34 I18N::translate('Descendants'); 35 } 36 37 /** {@inheritdoc} */ 38 public function getDescription() { 39 return /* I18N: Description of the “Descendants” module */ 40 I18N::translate('A sidebar showing the descendants of an individual.'); 41 } 42 43 /** 44 * @param Request $request 45 * 46 * @return Response 47 */ 48 public function getSearchAction(Request $request): Response { 49 /** @var Tree $tree */ 50 $tree = $request->attributes->get('tree'); 51 52 $search = $request->get('search', ''); 53 54 $html = ''; 55 56 if (strlen($search) >= 2) { 57 $rows = Database::prepare( 58 "SELECT i_id AS xref" . 59 " FROM `##individuals`" . 60 " JOIN `##name` ON i_id = n_id AND i_file = n_file" . 61 " WHERE n_sort LIKE CONCAT('%', :query, '%') AND i_file = :tree_id" . 62 " ORDER BY n_sort" 63 )->execute([ 64 'query' => $search, 65 'tree_id' => $tree->getTreeId(), 66 ])->fetchAll(); 67 68 foreach ($rows as $row) { 69 $individual = Individual::getInstance($row->xref, $tree); 70 if ($individual !== null && $individual->canShow()) { 71 $html .= $this->getPersonLi($individual); 72 } 73 } 74 } 75 76 if ($html !== '') { 77 $html = '<ul>' . $html . '</ul>'; 78 } 79 80 return new Response($html); 81 } 82 83 /** 84 * @param Request $request 85 * 86 * @return Response 87 */ 88 public function getDescendantsAction(Request $request): Response { 89 /** @var Tree $tree */ 90 $tree = $request->attributes->get('tree'); 91 92 $xref = $request->get('xref'); 93 94 $individual = Individual::getInstance($xref, $tree); 95 96 if ($individual !== null && $individual->canShow()) { 97 $html = $this->loadSpouses($individual, 1); 98 } else { 99 $html = ''; 100 } 101 102 return new Response($html); 103 } 104 105 /** {@inheritdoc} */ 106 public function defaultSidebarOrder() { 107 return 30; 108 } 109 110 /** {@inheritdoc} */ 111 public function hasSidebarContent(Individual $individual) { 112 return true; 113 } 114 115 /** 116 * Load this sidebar synchronously. 117 * 118 * @param Individual $individual 119 * 120 * @return string 121 */ 122 public function getSidebarContent(Individual $individual) { 123 return view('modules/descendancy/sidebar', [ 124 'individual_list' => $this->getPersonLi($individual, 1), 125 ]); 126 } 127 128 /** 129 * Format an individual in a list. 130 * 131 * @param Individual $person 132 * @param int $generations 133 * 134 * @return string 135 */ 136 public function getPersonLi(Individual $person, $generations = 0) { 137 $icon = $generations > 0 ? 'icon-minus' : 'icon-plus'; 138 $lifespan = $person->canShow() ? '(' . $person->getLifeSpan() . ')' : ''; 139 $spouses = $generations > 0 ? $this->loadSpouses($person, 0) : ''; 140 141 return 142 '<li class="sb_desc_indi_li">' . 143 '<a class="sb_desc_indi" href="' . e(route('module', ['module' => 'descendancy', 'action' => 'Descendants', 'ged' => $person->getTree()->getName(), 'xref' => $person->getXref()])) . '">' . 144 '<i class="plusminus ' . $icon . '"></i>' . 145 $person->getSexImage() . $person->getFullName() . $lifespan . 146 '</a>' . 147 FontAwesome::linkIcon('individual', $person->getFullName(), ['href' => $person->url()]) . 148 '<div>' . $spouses . '</div>' . 149 '</li>'; 150 } 151 152 /** 153 * Format a family in a list. 154 * 155 * @param Family $family 156 * @param Individual $person 157 * @param int $generations 158 * 159 * @return string 160 */ 161 public function getFamilyLi(Family $family, Individual $person, $generations = 0) { 162 $spouse = $family->getSpouse($person); 163 if ($spouse) { 164 $spouse_name = $spouse->getSexImage() . $spouse->getFullName(); 165 $spouse_link = FontAwesome::linkIcon('individual', $spouse->getFullName(), ['href' => $person->url()]); 166 } else { 167 $spouse_name = ''; 168 $spouse_link = ''; 169 } 170 171 $marryear = $family->getMarriageYear(); 172 $marr = $marryear ? '<i class="icon-rings"></i>' . $marryear : ''; 173 174 return 175 '<li class="sb_desc_indi_li">' . 176 '<a class="sb_desc_indi" href="#"><i class="plusminus icon-minus"></i>' . $spouse_name . $marr . '</a>' . 177 $spouse_link . 178 FontAwesome::linkIcon('family', $family->getFullName(), ['href' => $family->url()]) . 179 '<div>' . $this->loadChildren($family, $generations) . '</div>' . 180 '</li>'; 181 } 182 183 /** 184 * Display spouses. 185 * 186 * @param Individual $person 187 * @param int $generations 188 * 189 * @return string 190 */ 191 public function loadSpouses(Individual $person, $generations) { 192 $out = ''; 193 if ($person && $person->canShow()) { 194 foreach ($person->getSpouseFamilies() as $family) { 195 $out .= $this->getFamilyLi($family, $person, $generations - 1); 196 } 197 } 198 if ($out) { 199 return '<ul>' . $out . '</ul>'; 200 } else { 201 return ''; 202 } 203 } 204 205 /** 206 * Display descendants. 207 * 208 * @param Family $family 209 * @param int $generations 210 * 211 * @return string 212 */ 213 public function loadChildren(Family $family, $generations) { 214 $out = ''; 215 if ($family->canShow()) { 216 $children = $family->getChildren(); 217 if ($children) { 218 foreach ($children as $child) { 219 $out .= $this->getPersonLi($child, $generations - 1); 220 } 221 } else { 222 $out .= '<li class="sb_desc_none">' . I18N::translate('No children') . '</li>'; 223 } 224 } 225 if ($out) { 226 return '<ul>' . $out . '</ul>'; 227 } else { 228 return ''; 229 } 230 } 231} 232