1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* webtrees: online genealogy |
4
|
|
|
* Copyright (C) 2017 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
|
|
|
*/ |
16
|
|
|
namespace Fisharebest\Webtrees\Controller; |
17
|
|
|
|
18
|
|
|
use Fisharebest\Webtrees\Filter; |
19
|
|
|
use Fisharebest\Webtrees\Functions\FunctionsPrint; |
20
|
|
|
use Fisharebest\Webtrees\I18N; |
21
|
|
|
use Fisharebest\Webtrees\Individual; |
22
|
|
|
use Fisharebest\Webtrees\Theme; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Controller for the familybook chart |
26
|
|
|
*/ |
27
|
|
|
class FamilyBookController extends ChartController { |
28
|
|
|
/** @var string Whether to show spouse details '0' or '1' */ |
29
|
|
|
public $show_spouse; |
30
|
|
|
|
31
|
|
|
/** @var int Number of descendancy generations to show */ |
32
|
|
|
public $descent; |
33
|
|
|
|
34
|
|
|
/** @var int Number of ascendancy generations to show */ |
35
|
|
|
public $generations; |
36
|
|
|
|
37
|
|
|
/** @var int Number of descendancy generations that exist */ |
38
|
|
|
private $dgenerations; |
39
|
|
|
|
40
|
|
|
/** @var int Half height of personbox */ |
41
|
|
|
public $bhalfheight; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Create a family-book controller |
45
|
|
|
*/ |
46
|
|
|
public function __construct() { |
47
|
|
|
parent::__construct(); |
48
|
|
|
|
49
|
|
|
// Extract the request parameters |
50
|
|
|
$this->show_spouse = Filter::get('show_spouse', '[01]', '0'); |
51
|
|
|
$this->descent = Filter::getInteger('descent', 0, 9, 5); |
52
|
|
|
$this->generations = Filter::getInteger('generations', 2, $this->tree()->getPreference('MAX_DESCENDANCY_GENERATIONS'), 2); |
53
|
|
|
|
54
|
|
|
$this->bhalfheight = $this->getBoxDimensions()->height / 2; |
|
|
|
|
55
|
|
View Code Duplication |
if ($this->root && $this->root->canShowName()) { |
56
|
|
|
$this->setPageTitle( |
57
|
|
|
/* I18N: %s is an individual’s name */ |
58
|
|
|
I18N::translate('Family book of %s', $this->root->getFullName()) |
59
|
|
|
); |
60
|
|
|
} else { |
61
|
|
|
$this->setPageTitle(I18N::translate('Family book')); |
62
|
|
|
} |
63
|
|
|
//Checks how many generations of descendency is for the person for formatting purposes |
64
|
|
|
$this->dgenerations = $this->maxDescendencyGenerations($this->root->getXref(), 0); |
65
|
|
|
|
66
|
|
|
if ($this->dgenerations < 1) { |
67
|
|
|
$this->dgenerations = 1; |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Prints descendency of passed in person |
73
|
|
|
* |
74
|
|
|
* @param Individual|null $person |
75
|
|
|
* @param int $generation |
76
|
|
|
* |
77
|
|
|
* @return int |
|
|
|
|
78
|
|
|
*/ |
79
|
|
|
private function printDescendency(Individual $person = null, $generation) { |
80
|
|
|
if ($generation > $this->dgenerations) { |
81
|
|
|
return 0; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; |
85
|
|
|
$numkids = 0; |
86
|
|
|
|
87
|
|
|
// Load children |
88
|
|
|
$children = []; |
89
|
|
|
if ($person) { |
90
|
|
|
// Count is position from center to left, dgenerations is number of generations |
91
|
|
|
if ($generation < $this->dgenerations) { |
92
|
|
|
// All children, from all partners |
93
|
|
|
foreach ($person->getSpouseFamilies() as $family) { |
94
|
|
|
foreach ($family->getChildren() as $child) { |
95
|
|
|
$children[] = $child; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
if ($generation < $this->dgenerations) { |
101
|
|
|
if (!empty($children)) { |
102
|
|
|
// real people |
103
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
104
|
|
|
foreach ($children as $i => $child) { |
105
|
|
|
echo '<tr><td>'; |
106
|
|
|
$kids = $this->printDescendency($child, $generation + 1); |
107
|
|
|
$numkids += $kids; |
108
|
|
|
echo '</td>'; |
109
|
|
|
// Print the lines |
110
|
|
|
if (count($children) > 1) { |
111
|
|
|
if ($i === 0) { |
112
|
|
|
// Adjust for the first column on left |
113
|
|
|
$h = round(((($this->getBoxDimensions()->height) * $kids) + 8) / 2); // Assumes border = 1 and padding = 3 |
114
|
|
|
// Adjust for other vertical columns |
115
|
|
|
if ($kids > 1) { |
116
|
|
|
$h = ($kids - 1) * 4 + $h; |
117
|
|
|
} |
118
|
|
|
echo '<td class="align-bottom">', |
119
|
|
|
'<img id="vline_', $child->getXref(), '" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $h - 4, '"></td>'; |
|
|
|
|
120
|
|
|
} elseif ($i === count($children) - 1) { |
121
|
|
|
// Adjust for the first column on left |
122
|
|
|
$h = round(((($this->getBoxDimensions()->height) * $kids) + 8) / 2); |
123
|
|
|
// Adjust for other vertical columns |
124
|
|
|
if ($kids > 1) { |
125
|
|
|
$h = ($kids - 1) * 4 + $h; |
126
|
|
|
} |
127
|
|
|
echo '<td class="align-top">', |
128
|
|
|
'<img class="bvertline" width="3" id="vline_', $child->getXref(), '" src="', Theme::theme()->parameter('image-vline'), '" height="', $h - 2, '"></td>'; |
|
|
|
|
129
|
|
|
} else { |
130
|
|
|
echo '<td class="align-bottomm"style="background: url(', Theme::theme()->parameter('image-vline'), ');">', |
131
|
|
|
'<img class="spacer" width="3" src="', Theme::theme()->parameter('image-spacer'), '"></td>'; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
echo '</tr>'; |
135
|
|
|
} |
136
|
|
|
echo '</table>'; |
137
|
|
|
} else { |
138
|
|
|
// Hidden/empty boxes - to preserve the layout |
139
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; |
140
|
|
|
$numkids += $this->printDescendency(null, $generation + 1); |
141
|
|
|
echo '</td></tr></table>'; |
142
|
|
|
} |
143
|
|
|
echo '</td>'; |
144
|
|
|
echo '<td>'; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
if ($numkids === 0) { |
148
|
|
|
$numkids = 1; |
149
|
|
|
} |
150
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; |
151
|
|
|
if ($person) { |
152
|
|
|
FunctionsPrint::printPedigreePerson($person); |
153
|
|
|
echo '</td><td>', |
154
|
|
|
'<img class="linef1" src="', Theme::theme()->parameter('image-hline'), '" width="8" height="3">'; |
155
|
|
|
} else { |
156
|
|
|
echo '<div style="width:', $this->getBoxDimensions()->width + 19, 'px; height:', $this->getBoxDimensions()->height + 8, 'px;"></div>', |
157
|
|
|
'</td><td>'; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
// Print the spouse |
161
|
|
|
if ($generation === 1) { |
162
|
|
|
if ($this->show_spouse === '1') { |
163
|
|
|
foreach ($person->getSpouseFamilies() as $family) { |
|
|
|
|
164
|
|
|
$spouse = $family->getSpouse($person); |
165
|
|
|
echo '</td></tr><tr><td>'; |
166
|
|
|
FunctionsPrint::printPedigreePerson($spouse); |
167
|
|
|
$numkids += 0.95; |
168
|
|
|
echo '</td><td>'; |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
echo '</td></tr></table>'; |
173
|
|
|
echo '</td></tr>'; |
174
|
|
|
echo '</table>'; |
175
|
|
|
|
176
|
|
|
return $numkids; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Prints pedigree of the person passed in |
181
|
|
|
* |
182
|
|
|
* @param Individual $person |
183
|
|
|
* @param int $count |
184
|
|
|
*/ |
185
|
|
|
private function printPersonPedigree($person, $count) { |
186
|
|
|
if ($count >= $this->generations) { |
187
|
|
|
return; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
$genoffset = $this->generations; // handle pedigree n generations lines |
191
|
|
|
//-- calculate how tall the lines should be |
192
|
|
|
$lh = ($this->bhalfheight) * pow(2, ($genoffset - $count - 1)); |
193
|
|
|
// |
194
|
|
|
//Prints empty table columns for children w/o parents up to the max generation |
195
|
|
|
//This allows vertical line spacing to be consistent |
196
|
|
|
if (count($person->getChildFamilies()) == 0) { |
197
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
198
|
|
|
$this->printEmptyBox(); |
199
|
|
|
|
200
|
|
|
//-- recursively get the father’s family |
201
|
|
|
$this->printPersonPedigree($person, $count + 1); |
202
|
|
|
echo '</td><td></tr>'; |
203
|
|
|
$this->printEmptyBox(); |
204
|
|
|
|
205
|
|
|
//-- recursively get the mother’s family |
206
|
|
|
$this->printPersonPedigree($person, $count + 1); |
207
|
|
|
echo '</td><td></tr></table>'; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
// Empty box section done, now for regular pedigree |
211
|
|
|
foreach ($person->getChildFamilies() as $family) { |
212
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-bottom">'; |
213
|
|
|
// Determine line height for two or more spouces |
214
|
|
|
// And then adjust the vertical line for the root person only |
215
|
|
|
$famcount = 0; |
216
|
|
|
if ($this->show_spouse === '1') { |
217
|
|
|
// count number of spouses |
218
|
|
|
$famcount += count($person->getSpouseFamilies()); |
219
|
|
|
} |
220
|
|
|
$savlh = $lh; // Save current line height |
221
|
|
|
if ($count == 1 && $genoffset <= $famcount) { |
222
|
|
|
$linefactor = 0; |
223
|
|
|
// genoffset of 2 needs no adjustment |
224
|
|
|
if ($genoffset > 2) { |
225
|
|
|
$tblheight = $this->getBoxDimensions()->height + 8; |
226
|
|
|
if ($genoffset == 3) { |
227
|
|
|
if ($famcount == 3) { |
228
|
|
|
$linefactor = $tblheight / 2; |
229
|
|
|
} elseif ($famcount > 3) { |
230
|
|
|
$linefactor = $tblheight; |
231
|
|
|
} |
232
|
|
|
} |
233
|
|
View Code Duplication |
if ($genoffset == 4) { |
234
|
|
|
if ($famcount == 4) { |
235
|
|
|
$linefactor = $tblheight; |
236
|
|
|
} elseif ($famcount > 4) { |
237
|
|
|
$linefactor = ($famcount - $genoffset) * ($tblheight * 1.5); |
238
|
|
|
} |
239
|
|
|
} |
240
|
|
View Code Duplication |
if ($genoffset == 5) { |
241
|
|
|
if ($famcount == 5) { |
242
|
|
|
$linefactor = 0; |
243
|
|
|
} elseif ($famcount > 5) { |
244
|
|
|
$linefactor = $tblheight * ($famcount - $genoffset); |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
$lh = (($famcount - 1) * ($this->getBoxDimensions()->height) - ($linefactor)); |
249
|
|
|
if ($genoffset > 5) { |
250
|
|
|
$lh = $savlh; |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
echo '<img class="line3 pvline" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $lh, '"></td>', |
254
|
|
|
'<td>', |
255
|
|
|
'<img class="linef2" src="', Theme::theme()->parameter('image-hline'), '" height="3"></td>', |
256
|
|
|
'<td>'; |
257
|
|
|
$lh = $savlh; // restore original line height |
258
|
|
|
//-- print the father box |
259
|
|
|
FunctionsPrint::printPedigreePerson($family->getHusband()); |
260
|
|
|
echo '</td>'; |
261
|
|
|
if ($family->getHusband()) { |
262
|
|
|
echo '<td>'; |
263
|
|
|
//-- recursively get the father’s family |
264
|
|
|
$this->printPersonPedigree($family->getHusband(), $count + 1); |
265
|
|
|
echo '</td>'; |
266
|
|
|
} else { |
267
|
|
|
echo '<td>'; |
268
|
|
|
if ($genoffset > $count) { |
269
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
270
|
|
|
for ($i = 1; $i < (pow(2, ($genoffset) - $count) / 2); $i++) { |
271
|
|
|
$this->printEmptyBox(); |
272
|
|
|
echo '</tr>'; |
273
|
|
|
} |
274
|
|
|
echo '</table>'; |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
echo '</tr><tr>', |
278
|
|
|
'<td class="align-top"><img class="pvline" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $lh, '"></td>', |
279
|
|
|
'<td><img class="linef3" src="', Theme::theme()->parameter('image-hline'), '" height="3"></td>', |
280
|
|
|
'<td>'; |
281
|
|
|
//-- print the mother box |
282
|
|
|
FunctionsPrint::printPedigreePerson($family->getWife()); |
283
|
|
|
echo '</td>'; |
284
|
|
|
if ($family->getWife()) { |
285
|
|
|
echo '<td>'; |
286
|
|
|
//-- recursively print the mother’s family |
287
|
|
|
$this->printPersonPedigree($family->getWife(), $count + 1); |
288
|
|
|
echo '</td>'; |
289
|
|
View Code Duplication |
} else { |
290
|
|
|
echo '<td>'; |
291
|
|
|
if ($count < $genoffset - 1) { |
292
|
|
|
echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
293
|
|
|
for ($i = 1; $i < (pow(2, ($genoffset - 1) - $count) / 2) + 1; $i++) { |
294
|
|
|
$this->printEmptyBox(); |
295
|
|
|
echo '</tr>'; |
296
|
|
|
$this->printEmptyBox(); |
297
|
|
|
echo '</tr>'; |
298
|
|
|
} |
299
|
|
|
echo '</table>'; |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
echo '</tr>', |
303
|
|
|
'</table>'; |
304
|
|
|
break; |
305
|
|
|
} |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Calculates number of generations a person has |
310
|
|
|
* |
311
|
|
|
* @param string $pid |
312
|
|
|
* @param int $depth |
313
|
|
|
* |
314
|
|
|
* @return int |
315
|
|
|
*/ |
316
|
|
|
private function maxDescendencyGenerations($pid, $depth) { |
317
|
|
|
if ($depth > $this->generations) { |
318
|
|
|
return $depth; |
319
|
|
|
} |
320
|
|
|
$person = Individual::getInstance($pid, $this->tree()); |
321
|
|
|
if (is_null($person)) { |
322
|
|
|
return $depth; |
323
|
|
|
} |
324
|
|
|
$maxdc = $depth; |
325
|
|
|
foreach ($person->getSpouseFamilies() as $family) { |
326
|
|
View Code Duplication |
foreach ($family->getChildren() as $child) { |
327
|
|
|
$dc = $this->maxDescendencyGenerations($child->getXref(), $depth + 1); |
328
|
|
|
if ($dc >= $this->generations) { |
329
|
|
|
return $dc; |
330
|
|
|
} |
331
|
|
|
if ($dc > $maxdc) { |
332
|
|
|
$maxdc = $dc; |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
$maxdc++; |
337
|
|
|
if ($maxdc == 1) { |
338
|
|
|
$maxdc++; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
return $maxdc; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Print empty box |
346
|
|
|
*/ |
347
|
|
|
|
348
|
|
|
private function printEmptyBox() { |
349
|
|
|
echo Theme::theme()->individualBoxEmpty(); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Print a “Family Book” for an individual |
354
|
|
|
* |
355
|
|
|
* @param Individual $person |
356
|
|
|
* @param int $descent_steps |
357
|
|
|
*/ |
358
|
|
|
public function printFamilyBook(Individual $person, $descent_steps) { |
359
|
|
|
if ($descent_steps == 0) { |
360
|
|
|
return; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
$families = $person->getSpouseFamilies(); |
364
|
|
|
if (1 || !empty($families)) { |
365
|
|
|
echo |
366
|
|
|
'<h3>', |
367
|
|
|
/* I18N: %s is an individual’s name */ I18N::translate('Family of %s', $person->getFullName()), |
368
|
|
|
'</h3>', |
369
|
|
|
'<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-middle">'; |
370
|
|
|
$this->dgenerations = $this->generations; |
371
|
|
|
$this->printDescendency($person, 1); |
372
|
|
|
echo '</td><td class="align-middle">'; |
373
|
|
|
$this->printPersonPedigree($person, 1); |
374
|
|
|
echo '</td></tr></table><br><br><hr class="family-break"><br><br>'; |
375
|
|
|
foreach ($families as $family) { |
376
|
|
|
foreach ($family->getChildren() as $child) { |
377
|
|
|
$this->printFamilyBook($child, $descent_steps - 1); |
378
|
|
|
} |
379
|
|
|
} |
380
|
|
|
} |
381
|
|
|
} |
382
|
|
|
} |
383
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.