Total Complexity | 994 |
Total Lines | 7283 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Stats often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Stats, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
36 | class Stats |
||
37 | { |
||
38 | /** @var Tree Generate statistics for a specified tree. */ |
||
39 | private $tree; |
||
40 | |||
41 | /** @var string[] All public functions are available as keywords - except these ones */ |
||
42 | private $public_but_not_allowed = array( |
||
43 | '__construct', 'embedTags', 'iso3166', 'getAllCountries', 'getAllTagsTable', 'getAllTagsText', 'statsPlaces', 'statsBirthQuery', 'statsDeathQuery', 'statsMarrQuery', 'statsAgeQuery', 'monthFirstChildQuery', 'statsChildrenQuery', 'statsMarrAgeQuery', |
||
44 | ); |
||
45 | |||
46 | /** @var string[] List of GEDCOM media types */ |
||
47 | private $_media_types = array('audio', 'book', 'card', 'certificate', 'coat', 'document', 'electronic', 'magazine', 'manuscript', 'map', 'fiche', 'film', 'newspaper', 'painting', 'photo', 'tombstone', 'video', 'other'); |
||
48 | |||
49 | /** |
||
50 | * Create the statistics for a tree. |
||
51 | * |
||
52 | * @param Tree $tree Generate statistics for this tree |
||
53 | */ |
||
54 | public function __construct(Tree $tree) |
||
55 | { |
||
56 | $this->tree = $tree; |
||
57 | } |
||
58 | |||
59 | /** |
||
60 | * Return a string of all supported tags and an example of its output in table row form. |
||
61 | * |
||
62 | * @return string |
||
63 | */ |
||
64 | public function getAllTagsTable() |
||
65 | { |
||
66 | $examples = array(); |
||
67 | foreach (get_class_methods($this) as $method) { |
||
68 | $reflection = new \ReflectionMethod($this, $method); |
||
69 | if ($reflection->isPublic() && !in_array($method, $this->public_but_not_allowed)) { |
||
70 | $examples[$method] = $this->$method(); |
||
71 | } |
||
72 | } |
||
73 | ksort($examples); |
||
74 | |||
75 | $html = ''; |
||
76 | foreach ($examples as $tag => $value) { |
||
77 | $html .= '<tr>'; |
||
78 | $html .= '<td class="list_value_wrap">' . $tag . '</td>'; |
||
79 | $html .= '<td class="list_value_wrap">' . $value . '</td>'; |
||
80 | $html .= '</tr>'; |
||
81 | } |
||
82 | |||
83 | return |
||
84 | '<table id="keywords" style="width:100%; table-layout:fixed"><thead>' . |
||
85 | '<tr>' . |
||
86 | '<th class="list_label_wrap width25">' . |
||
87 | I18N::translate('Embedded variable') . |
||
88 | '</th>' . |
||
89 | '<th class="list_label_wrap width75">' . |
||
90 | I18N::translate('Resulting value') . |
||
91 | '</th>' . |
||
92 | '</tr>' . |
||
93 | '</thead><tbody>' . |
||
94 | $html . |
||
95 | '</tbody></table>'; |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * Return a string of all supported tags in plain text. |
||
100 | * |
||
101 | * @return string |
||
102 | */ |
||
103 | public function getAllTagsText() |
||
104 | { |
||
105 | $examples = array(); |
||
106 | foreach (get_class_methods($this) as $method) { |
||
107 | $reflection = new \ReflectionMethod($this, $method); |
||
108 | if ($reflection->isPublic() && !in_array($method, $this->public_but_not_allowed)) { |
||
109 | $examples[$method] = $method; |
||
110 | } |
||
111 | } |
||
112 | ksort($examples); |
||
113 | |||
114 | return implode('<br>', $examples); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Get tags and their parsed results. |
||
119 | * |
||
120 | * @param string $text |
||
121 | * |
||
122 | * @return string[][] |
||
123 | */ |
||
124 | private function getTags($text) |
||
125 | { |
||
126 | static $funcs; |
||
127 | |||
128 | // Retrive all class methods |
||
129 | isset($funcs) or $funcs = get_class_methods($this); |
||
130 | |||
131 | // Extract all tags from the provided text |
||
132 | preg_match_all("/#([^#]+)(?=#)/", (string) $text, $match); |
||
133 | $tags = $match[1]; |
||
134 | $c = count($tags); |
||
135 | $new_tags = array(); // tag to replace |
||
136 | $new_values = array(); // value to replace it with |
||
137 | |||
138 | /* |
||
139 | * Parse block tags. |
||
140 | */ |
||
141 | for ($i = 0; $i < $c; $i++) { |
||
142 | $full_tag = $tags[$i]; |
||
143 | // Added for new parameter support |
||
144 | $params = explode(':', $tags[$i]); |
||
145 | if (count($params) > 1) { |
||
146 | $tags[$i] = array_shift($params); |
||
147 | } else { |
||
148 | $params = array(); |
||
149 | } |
||
150 | |||
151 | // Generate the replacement value for the tag |
||
152 | if (method_exists($this, $tags[$i])) { |
||
153 | $new_tags[] = "#{$full_tag}#"; |
||
154 | $new_values[] = call_user_func_array(array($this, $tags[$i]), array($params)); |
||
155 | } |
||
156 | } |
||
157 | |||
158 | return array($new_tags, $new_values); |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Embed tags in text |
||
163 | * |
||
164 | * @param string $text |
||
165 | * |
||
166 | * @return string |
||
167 | */ |
||
168 | public function embedTags($text) |
||
169 | { |
||
170 | if (strpos($text, '#') !== false) { |
||
171 | list($new_tags, $new_values) = $this->getTags($text); |
||
172 | $text = str_replace($new_tags, $new_values, $text); |
||
173 | } |
||
174 | |||
175 | return $text; |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Get the name used for GEDCOM files and URLs. |
||
180 | * |
||
181 | * @return string |
||
182 | */ |
||
183 | public function gedcomFilename() |
||
184 | { |
||
185 | return $this->tree->getName(); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Get the internal ID number of the tree. |
||
190 | * |
||
191 | * @return int |
||
192 | */ |
||
193 | public function gedcomId() |
||
194 | { |
||
195 | return $this->tree->getTreeId(); |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * Get the descriptive title of the tree. |
||
200 | * |
||
201 | * @return string |
||
202 | */ |
||
203 | public function gedcomTitle() |
||
204 | { |
||
205 | return $this->tree->getTitleHtml(); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * Get information from the GEDCOM's HEAD record. |
||
210 | * |
||
211 | * @return string[] |
||
212 | */ |
||
213 | private function gedcomHead() |
||
214 | { |
||
215 | $title = ''; |
||
216 | $version = ''; |
||
217 | $source = ''; |
||
218 | |||
219 | $head = GedcomRecord::getInstance('HEAD', $this->tree); |
||
220 | $sour = $head->getFirstFact('SOUR'); |
||
221 | if ($sour) { |
||
222 | $source = $sour->getValue(); |
||
223 | $title = $sour->getAttribute('NAME'); |
||
224 | $version = $sour->getAttribute('VERS'); |
||
225 | } |
||
226 | |||
227 | return array($title, $version, $source); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Get the software originally used to create the GEDCOM file. |
||
232 | * |
||
233 | * @return string |
||
234 | */ |
||
235 | public function gedcomCreatedSoftware() |
||
236 | { |
||
237 | $head = $this->gedcomHead(); |
||
238 | |||
239 | return $head[0]; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Get the version of software which created the GEDCOM file. |
||
244 | * |
||
245 | * @return string |
||
246 | */ |
||
247 | public function gedcomCreatedVersion() |
||
248 | { |
||
249 | $head = $this->gedcomHead(); |
||
250 | // fix broken version string in Family Tree Maker |
||
251 | if (strstr($head[1], 'Family Tree Maker ')) { |
||
252 | $p = strpos($head[1], '(') + 1; |
||
253 | $p2 = strpos($head[1], ')'); |
||
254 | $head[1] = substr($head[1], $p, ($p2 - $p)); |
||
255 | } |
||
256 | // Fix EasyTree version |
||
257 | if ($head[2] == 'EasyTree') { |
||
258 | $head[1] = substr($head[1], 1); |
||
259 | } |
||
260 | |||
261 | return $head[1]; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Get the date the GEDCOM file was created. |
||
266 | * |
||
267 | * @return string |
||
268 | */ |
||
269 | public function gedcomDate() |
||
270 | { |
||
271 | $head = GedcomRecord::getInstance('HEAD', $this->tree); |
||
272 | $fact = $head->getFirstFact('DATE'); |
||
273 | if ($fact) { |
||
274 | $date = new Date($fact->getValue()); |
||
275 | |||
276 | return $date->display(); |
||
277 | } |
||
278 | |||
279 | return ''; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * When was this tree last updated? |
||
284 | * |
||
285 | * @return string |
||
286 | */ |
||
287 | public function gedcomUpdated() |
||
288 | { |
||
289 | $row = Database::prepare( |
||
290 | "SELECT d_year, d_month, d_day FROM `##dates` WHERE d_julianday1 = (SELECT MAX(d_julianday1) FROM `##dates` WHERE d_file =? AND d_fact='CHAN') LIMIT 1" |
||
291 | )->execute(array($this->tree->getTreeId()))->fetchOneRow(); |
||
292 | if ($row) { |
||
293 | $date = new Date("{$row->d_day} {$row->d_month} {$row->d_year}"); |
||
294 | |||
295 | return $date->display(); |
||
296 | } else { |
||
297 | return $this->gedcomDate(); |
||
298 | } |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * What is the significant individual from this tree? |
||
303 | * |
||
304 | * @return string |
||
305 | */ |
||
306 | public function gedcomRootId() |
||
307 | { |
||
308 | return $this->tree->getPreference('PEDIGREE_ROOT_ID'); |
||
309 | } |
||
310 | |||
311 | /** |
||
312 | * Convert totals into percentages. |
||
313 | * |
||
314 | * @param string $total |
||
315 | * @param string $type |
||
316 | * |
||
317 | * @return string |
||
318 | */ |
||
319 | private function getPercentage($total, $type) |
||
320 | { |
||
321 | switch ($type) { |
||
322 | case 'individual': |
||
323 | $type = $this->totalIndividualsQuery(); |
||
324 | break; |
||
325 | case 'family': |
||
326 | $type = $this->totalFamiliesQuery(); |
||
327 | break; |
||
328 | case 'source': |
||
329 | $type = $this->totalSourcesQuery(); |
||
330 | break; |
||
331 | case 'note': |
||
332 | $type = $this->totalNotesQuery(); |
||
333 | break; |
||
334 | case 'all': |
||
335 | default: |
||
336 | $type = $this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery(); |
||
337 | break; |
||
338 | } |
||
339 | if ($type == 0) { |
||
340 | return I18N::percentage(0, 1); |
||
341 | } else { |
||
342 | return I18N::percentage($total / $type, 1); |
||
343 | } |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * How many GEDCOM records exist in the tree. |
||
348 | * |
||
349 | * @return string |
||
350 | */ |
||
351 | public function totalRecords() |
||
352 | { |
||
353 | return I18N::number($this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery()); |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * How many individuals exist in the tree. |
||
358 | * |
||
359 | * @return int |
||
360 | */ |
||
361 | private function totalIndividualsQuery() |
||
362 | { |
||
363 | return (int) Database::prepare( |
||
364 | "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id" |
||
365 | )->execute(array( |
||
366 | 'tree_id' => $this->tree->getTreeId(), |
||
367 | ))->fetchOne(); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * How many individuals exist in the tree. |
||
372 | * |
||
373 | * @return string |
||
374 | */ |
||
375 | public function totalIndividuals() |
||
376 | { |
||
377 | return I18N::number($this->totalIndividualsQuery()); |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * How many individuals have one or more sources. |
||
382 | * |
||
383 | * @return int |
||
384 | */ |
||
385 | private function totalIndisWithSourcesQuery() |
||
386 | { |
||
387 | return (int) Database::prepare( |
||
388 | "SELECT COUNT(DISTINCT i_id)" . |
||
389 | " FROM `##individuals` JOIN `##link` ON i_id = l_from AND i_file = l_file" . |
||
390 | " WHERE l_file = :tree_id AND l_type = 'SOUR'" |
||
391 | )->execute(array( |
||
392 | 'tree_id' => $this->tree->getTreeId(), |
||
393 | ))->fetchOne(); |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * How many individuals have one or more sources. |
||
398 | * |
||
399 | * @return string |
||
400 | */ |
||
401 | public function totalIndisWithSources() |
||
402 | { |
||
403 | return I18N::number($this->totalIndisWithSourcesQuery()); |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * Create a chart showing individuals with/without sources. |
||
408 | * |
||
409 | * @param string[] $params |
||
410 | * |
||
411 | * @return string |
||
412 | */ |
||
413 | public function chartIndisWithSources($params = array()) |
||
414 | { |
||
415 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
416 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
417 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
418 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
419 | |||
420 | if (isset($params[0]) && $params[0] != '') { |
||
421 | $size = strtolower($params[0]); |
||
422 | } else { |
||
423 | $size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y; |
||
424 | } |
||
425 | if (isset($params[1]) && $params[1] != '') { |
||
426 | $color_from = strtolower($params[1]); |
||
427 | } else { |
||
428 | $color_from = $WT_STATS_CHART_COLOR1; |
||
429 | } |
||
430 | if (isset($params[2]) && $params[2] != '') { |
||
431 | $color_to = strtolower($params[2]); |
||
432 | } else { |
||
433 | $color_to = $WT_STATS_CHART_COLOR2; |
||
434 | } |
||
435 | $sizes = explode('x', $size); |
||
436 | $tot_indi = $this->totalIndividualsQuery(); |
||
437 | if ($tot_indi == 0) { |
||
438 | return ''; |
||
439 | } else { |
||
440 | $tot_sindi_per = round($this->totalIndisWithSourcesQuery() / $tot_indi, 3); |
||
441 | $chd = $this->arrayToExtendedEncoding(array(100 - 100 * $tot_sindi_per, 100 * $tot_sindi_per)); |
||
|
|||
442 | $chl = I18N::translate('Without sources') . ' - ' . I18N::percentage(1 - $tot_sindi_per, 1) . '|' . |
||
443 | I18N::translate('With sources') . ' - ' . I18N::percentage($tot_sindi_per, 1); |
||
444 | $chart_title = I18N::translate('Individuals with sources'); |
||
445 | |||
446 | return '<img src="https://chart.googleapis.com/chart?cht=p3&chd=e:' . $chd . '&chs=' . $size . '&chco=' . $color_from . ',' . $color_to . '&chf=bg,s,ffffff00&chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '">'; |
||
447 | } |
||
448 | } |
||
449 | |||
450 | /** |
||
451 | * Show the total individuals as a percentage. |
||
452 | * |
||
453 | * @return string |
||
454 | */ |
||
455 | public function totalIndividualsPercentage() |
||
456 | { |
||
457 | return $this->getPercentage($this->totalIndividualsQuery(), 'all'); |
||
458 | } |
||
459 | |||
460 | /** |
||
461 | * Count the total families. |
||
462 | * |
||
463 | * @return int |
||
464 | */ |
||
465 | private function totalFamiliesQuery() |
||
466 | { |
||
467 | return (int) Database::prepare( |
||
468 | "SELECT COUNT(*) FROM `##families` WHERE f_file = :tree_id" |
||
469 | )->execute(array( |
||
470 | 'tree_id' => $this->tree->getTreeId(), |
||
471 | ))->fetchOne(); |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * Count the total families. |
||
476 | * |
||
477 | * @return string |
||
478 | */ |
||
479 | public function totalFamilies() |
||
480 | { |
||
481 | return I18N::number($this->totalFamiliesQuery()); |
||
482 | } |
||
483 | |||
484 | /** |
||
485 | * Count the families with source records. |
||
486 | * |
||
487 | * @return int |
||
488 | */ |
||
489 | private function totalFamsWithSourcesQuery() |
||
490 | { |
||
491 | return (int) Database::prepare( |
||
492 | "SELECT COUNT(DISTINCT f_id)" . |
||
493 | " FROM `##families` JOIN `##link` ON f_id = l_from AND f_file = l_file" . |
||
494 | " WHERE l_file = :tree_id AND l_type = 'SOUR'" |
||
495 | )->execute(array( |
||
496 | 'tree_id' => $this->tree->getTreeId(), |
||
497 | ))->fetchOne(); |
||
498 | } |
||
499 | |||
500 | /** |
||
501 | * Count the families with with source records. |
||
502 | * |
||
503 | * @return string |
||
504 | */ |
||
505 | public function totalFamsWithSources() |
||
506 | { |
||
507 | return I18N::number($this->totalFamsWithSourcesQuery()); |
||
508 | } |
||
509 | |||
510 | /** |
||
511 | * Create a chart of individuals with/without sources. |
||
512 | * |
||
513 | * @param string[] $params |
||
514 | * |
||
515 | * @return string |
||
516 | */ |
||
517 | public function chartFamsWithSources($params = array()) |
||
518 | { |
||
519 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
520 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
521 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
522 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
523 | |||
524 | if (isset($params[0]) && $params[0] != '') { |
||
525 | $size = strtolower($params[0]); |
||
526 | } else { |
||
527 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
528 | } |
||
529 | if (isset($params[1]) && $params[1] != '') { |
||
530 | $color_from = strtolower($params[1]); |
||
531 | } else { |
||
532 | $color_from = $WT_STATS_CHART_COLOR1; |
||
533 | } |
||
534 | if (isset($params[2]) && $params[2] != '') { |
||
535 | $color_to = strtolower($params[2]); |
||
536 | } else { |
||
537 | $color_to = $WT_STATS_CHART_COLOR2; |
||
538 | } |
||
539 | $sizes = explode('x', $size); |
||
540 | $tot_fam = $this->totalFamiliesQuery(); |
||
541 | if ($tot_fam == 0) { |
||
542 | return ''; |
||
543 | } else { |
||
544 | $tot_sfam_per = round($this->totalFamsWithSourcesQuery() / $tot_fam, 3); |
||
545 | $chd = $this->arrayToExtendedEncoding(array(100 - 100 * $tot_sfam_per, 100 * $tot_sfam_per)); |
||
546 | $chl = I18N::translate('Without sources') . ' - ' . I18N::percentage(1 - $tot_sfam_per, 1) . '|' . |
||
547 | I18N::translate('With sources') . ' - ' . I18N::percentage($tot_sfam_per, 1); |
||
548 | $chart_title = I18N::translate('Families with sources'); |
||
549 | |||
550 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; |
||
551 | } |
||
552 | } |
||
553 | |||
554 | /** |
||
555 | * Show the total families as a percentage. |
||
556 | * |
||
557 | * @return string |
||
558 | */ |
||
559 | public function totalFamiliesPercentage() |
||
560 | { |
||
561 | return $this->getPercentage($this->totalFamiliesQuery(), 'all'); |
||
562 | } |
||
563 | |||
564 | /** |
||
565 | * Count the total number of sources. |
||
566 | * |
||
567 | * @return int |
||
568 | */ |
||
569 | private function totalSourcesQuery() |
||
570 | { |
||
571 | return (int) Database::prepare( |
||
572 | "SELECT COUNT(*) FROM `##sources` WHERE s_file = :tree_id" |
||
573 | )->execute(array( |
||
574 | 'tree_id' => $this->tree->getTreeId(), |
||
575 | ))->fetchOne(); |
||
576 | } |
||
577 | |||
578 | /** |
||
579 | * Count the total number of sources. |
||
580 | * |
||
581 | * @return string |
||
582 | */ |
||
583 | public function totalSources() |
||
584 | { |
||
585 | return I18N::number($this->totalSourcesQuery()); |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * Show the number of sources as a percentage. |
||
590 | * |
||
591 | * @return string |
||
592 | */ |
||
593 | public function totalSourcesPercentage() |
||
594 | { |
||
595 | return $this->getPercentage($this->totalSourcesQuery(), 'all'); |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * Count the number of notes. |
||
600 | * |
||
601 | * @return int |
||
602 | */ |
||
603 | private function totalNotesQuery() |
||
604 | { |
||
605 | return (int) Database::prepare( |
||
606 | "SELECT COUNT(*) FROM `##other` WHERE o_type='NOTE' AND o_file = :tree_id" |
||
607 | )->execute(array( |
||
608 | 'tree_id' => $this->tree->getTreeId(), |
||
609 | ))->fetchOne(); |
||
610 | } |
||
611 | |||
612 | /** |
||
613 | * Count the number of notes. |
||
614 | * |
||
615 | * @return string |
||
616 | */ |
||
617 | public function totalNotes() |
||
618 | { |
||
619 | return I18N::number($this->totalNotesQuery()); |
||
620 | } |
||
621 | |||
622 | /** |
||
623 | * Show the number of notes as a percentage. |
||
624 | * |
||
625 | * @return string |
||
626 | */ |
||
627 | public function totalNotesPercentage() |
||
628 | { |
||
629 | return $this->getPercentage($this->totalNotesQuery(), 'all'); |
||
630 | } |
||
631 | |||
632 | /** |
||
633 | * Count the number of repositories. |
||
634 | * |
||
635 | * @return int |
||
636 | */ |
||
637 | private function totalRepositoriesQuery() |
||
638 | { |
||
639 | return (int) Database::prepare( |
||
640 | "SELECT COUNT(*) FROM `##other` WHERE o_type='REPO' AND o_file = :tree_id" |
||
641 | )->execute(array( |
||
642 | 'tree_id' => $this->tree->getTreeId(), |
||
643 | ))->fetchOne(); |
||
644 | } |
||
645 | |||
646 | /** |
||
647 | * Count the number of repositories |
||
648 | * |
||
649 | * @return string |
||
650 | */ |
||
651 | public function totalRepositories() |
||
652 | { |
||
653 | return I18N::number($this->totalRepositoriesQuery()); |
||
654 | } |
||
655 | |||
656 | /** |
||
657 | * Show the total number of repositories as a percentage. |
||
658 | * |
||
659 | * @return string |
||
660 | */ |
||
661 | public function totalRepositoriesPercentage() |
||
662 | { |
||
663 | return $this->getPercentage($this->totalRepositoriesQuery(), 'all'); |
||
664 | } |
||
665 | |||
666 | /** |
||
667 | * Count the surnames. |
||
668 | * |
||
669 | * @param string[] $params |
||
670 | * |
||
671 | * @return string |
||
672 | */ |
||
673 | public function totalSurnames($params = array()) |
||
674 | { |
||
675 | if ($params) { |
||
676 | $opt = 'IN (' . implode(',', array_fill(0, count($params), '?')) . ')'; |
||
677 | $distinct = ''; |
||
678 | } else { |
||
679 | $opt = "IS NOT NULL"; |
||
680 | $distinct = 'DISTINCT'; |
||
681 | } |
||
682 | $params[] = $this->tree->getTreeId(); |
||
683 | $total = |
||
684 | Database::prepare( |
||
685 | "SELECT COUNT({$distinct} n_surn COLLATE '" . I18N::collation() . "')" . |
||
686 | " FROM `##name`" . |
||
687 | " WHERE n_surn COLLATE '" . I18N::collation() . "' {$opt} AND n_file=?" |
||
688 | )->execute( |
||
689 | $params |
||
690 | )->fetchOne(); |
||
691 | |||
692 | return I18N::number($total); |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * Count the number of distinct given names, or count the number of |
||
697 | * occurrences of a specific name or names. |
||
698 | * |
||
699 | * @param string[] $params |
||
700 | * |
||
701 | * @return string |
||
702 | */ |
||
703 | public function totalGivennames($params = array()) |
||
704 | { |
||
705 | if ($params) { |
||
706 | $qs = implode(',', array_fill(0, count($params), '?')); |
||
707 | $params[] = $this->tree->getTreeId(); |
||
708 | $total = |
||
709 | Database::prepare("SELECT COUNT( n_givn) FROM `##name` WHERE n_givn IN ({$qs}) AND n_file=?") |
||
710 | ->execute($params) |
||
711 | ->fetchOne(); |
||
712 | } else { |
||
713 | $total = |
||
714 | Database::prepare("SELECT COUNT(DISTINCT n_givn) FROM `##name` WHERE n_givn IS NOT NULL AND n_file=?") |
||
715 | ->execute(array($this->tree->getTreeId())) |
||
716 | ->fetchOne(); |
||
717 | } |
||
718 | |||
719 | return I18N::number($total); |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * Count the number of events (with dates). |
||
724 | * |
||
725 | * @param string[] $params |
||
726 | * |
||
727 | * @return string |
||
728 | */ |
||
729 | public function totalEvents($params = array()) |
||
730 | { |
||
731 | $sql = "SELECT COUNT(*) AS tot FROM `##dates` WHERE d_file=?"; |
||
732 | $vars = array($this->tree->getTreeId()); |
||
733 | |||
734 | $no_types = array('HEAD', 'CHAN'); |
||
735 | if ($params) { |
||
736 | $types = array(); |
||
737 | foreach ($params as $type) { |
||
738 | if (substr($type, 0, 1) == '!') { |
||
739 | $no_types[] = substr($type, 1); |
||
740 | } else { |
||
741 | $types[] = $type; |
||
742 | } |
||
743 | } |
||
744 | if ($types) { |
||
745 | $sql .= ' AND d_fact IN (' . implode(', ', array_fill(0, count($types), '?')) . ')'; |
||
746 | $vars = array_merge($vars, $types); |
||
747 | } |
||
748 | } |
||
749 | $sql .= ' AND d_fact NOT IN (' . implode(', ', array_fill(0, count($no_types), '?')) . ')'; |
||
750 | $vars = array_merge($vars, $no_types); |
||
751 | |||
752 | return I18N::number(Database::prepare($sql)->execute($vars)->fetchOne()); |
||
753 | } |
||
754 | |||
755 | /** |
||
756 | * Count the number of births. |
||
757 | * |
||
758 | * @return string |
||
759 | */ |
||
760 | public function totalEventsBirth() |
||
761 | { |
||
762 | return $this->totalEvents(explode('|', WT_EVENTS_BIRT)); |
||
763 | } |
||
764 | |||
765 | /** |
||
766 | * Count the number of births. |
||
767 | * |
||
768 | * @return string |
||
769 | */ |
||
770 | public function totalBirths() |
||
771 | { |
||
772 | return $this->totalEvents(array('BIRT')); |
||
773 | } |
||
774 | |||
775 | /** |
||
776 | * Count the number of deaths. |
||
777 | * |
||
778 | * @return string |
||
779 | */ |
||
780 | public function totalEventsDeath() |
||
781 | { |
||
782 | return $this->totalEvents(explode('|', WT_EVENTS_DEAT)); |
||
783 | } |
||
784 | |||
785 | /** |
||
786 | * Count the number of deaths. |
||
787 | * |
||
788 | * @return string |
||
789 | */ |
||
790 | public function totalDeaths() |
||
791 | { |
||
792 | return $this->totalEvents(array('DEAT')); |
||
793 | } |
||
794 | |||
795 | /** |
||
796 | * Count the number of marriages. |
||
797 | * |
||
798 | * @return string |
||
799 | */ |
||
800 | public function totalEventsMarriage() |
||
801 | { |
||
802 | return $this->totalEvents(explode('|', WT_EVENTS_MARR)); |
||
803 | } |
||
804 | |||
805 | /** |
||
806 | * Count the number of marriages. |
||
807 | * |
||
808 | * @return string |
||
809 | */ |
||
810 | public function totalMarriages() |
||
811 | { |
||
812 | return $this->totalEvents(array('MARR')); |
||
813 | } |
||
814 | |||
815 | /** |
||
816 | * Count the number of divorces. |
||
817 | * |
||
818 | * @return string |
||
819 | */ |
||
820 | public function totalEventsDivorce() |
||
821 | { |
||
822 | return $this->totalEvents(explode('|', WT_EVENTS_DIV)); |
||
823 | } |
||
824 | |||
825 | /** |
||
826 | * Count the number of divorces. |
||
827 | * |
||
828 | * @return string |
||
829 | */ |
||
830 | public function totalDivorces() |
||
831 | { |
||
832 | return $this->totalEvents(array('DIV')); |
||
833 | } |
||
834 | |||
835 | /** |
||
836 | * Count the number of other events. |
||
837 | * |
||
838 | * @return string |
||
839 | */ |
||
840 | public function totalEventsOther() |
||
841 | { |
||
842 | $facts = array_merge(explode('|', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT)); |
||
843 | $no_facts = array(); |
||
844 | foreach ($facts as $fact) { |
||
845 | $fact = '!' . str_replace('\'', '', $fact); |
||
846 | $no_facts[] = $fact; |
||
847 | } |
||
848 | |||
849 | return $this->totalEvents($no_facts); |
||
850 | } |
||
851 | |||
852 | /** |
||
853 | * Count the number of males. |
||
854 | * |
||
855 | * @return int |
||
856 | */ |
||
857 | private function totalSexMalesQuery() |
||
858 | { |
||
859 | return (int) Database::prepare( |
||
860 | "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'M'" |
||
861 | )->execute(array( |
||
862 | 'tree_id' => $this->tree->getTreeId(), |
||
863 | ))->fetchOne(); |
||
864 | } |
||
865 | |||
866 | /** |
||
867 | * Count the number of males. |
||
868 | * |
||
869 | * @return string |
||
870 | */ |
||
871 | public function totalSexMales() |
||
872 | { |
||
873 | return I18N::number($this->totalSexMalesQuery()); |
||
874 | } |
||
875 | |||
876 | /** |
||
877 | * Count the number of males |
||
878 | * |
||
879 | * @return string |
||
880 | */ |
||
881 | public function totalSexMalesPercentage() |
||
882 | { |
||
883 | return $this->getPercentage($this->totalSexMalesQuery(), 'individual'); |
||
884 | } |
||
885 | |||
886 | /** |
||
887 | * Count the number of females. |
||
888 | * |
||
889 | * @return int |
||
890 | */ |
||
891 | private function totalSexFemalesQuery() |
||
892 | { |
||
893 | return (int) Database::prepare( |
||
894 | "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'F'" |
||
895 | )->execute(array( |
||
896 | 'tree_id' => $this->tree->getTreeId(), |
||
897 | ))->fetchOne(); |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * Count the number of females. |
||
902 | * |
||
903 | * @return string |
||
904 | */ |
||
905 | public function totalSexFemales() |
||
906 | { |
||
907 | return I18N::number($this->totalSexFemalesQuery()); |
||
908 | } |
||
909 | |||
910 | /** |
||
911 | * Count the number of females. |
||
912 | * |
||
913 | * @return string |
||
914 | */ |
||
915 | public function totalSexFemalesPercentage() |
||
916 | { |
||
917 | return $this->getPercentage($this->totalSexFemalesQuery(), 'individual'); |
||
918 | } |
||
919 | |||
920 | /** |
||
921 | * Count the number of individuals with unknown sex. |
||
922 | * |
||
923 | * @return int |
||
924 | */ |
||
925 | private function totalSexUnknownQuery() |
||
926 | { |
||
927 | return (int) Database::prepare( |
||
928 | "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'U'" |
||
929 | )->execute(array( |
||
930 | 'tree_id' => $this->tree->getTreeId(), |
||
931 | ))->fetchOne(); |
||
932 | } |
||
933 | |||
934 | /** |
||
935 | * Count the number of individuals with unknown sex. |
||
936 | * |
||
937 | * @return string |
||
938 | */ |
||
939 | public function totalSexUnknown() |
||
940 | { |
||
941 | return I18N::number($this->totalSexUnknownQuery()); |
||
942 | } |
||
943 | |||
944 | /** |
||
945 | * Count the number of individuals with unknown sex. |
||
946 | * |
||
947 | * @return string |
||
948 | */ |
||
949 | public function totalSexUnknownPercentage() |
||
950 | { |
||
951 | return $this->getPercentage($this->totalSexUnknownQuery(), 'individual'); |
||
952 | } |
||
953 | |||
954 | /** |
||
955 | * Generate a chart showing sex distribution. |
||
956 | * |
||
957 | * @param string[] $params |
||
958 | * |
||
959 | * @return string |
||
960 | */ |
||
961 | public function chartSex($params = array()) |
||
962 | { |
||
963 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
964 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
965 | |||
966 | if (isset($params[0]) && $params[0] != '') { |
||
967 | $size = strtolower($params[0]); |
||
968 | } else { |
||
969 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
970 | } |
||
971 | if (isset($params[1]) && $params[1] != '') { |
||
972 | $color_female = strtolower($params[1]); |
||
973 | } else { |
||
974 | $color_female = 'ffd1dc'; |
||
975 | } |
||
976 | if (isset($params[2]) && $params[2] != '') { |
||
977 | $color_male = strtolower($params[2]); |
||
978 | } else { |
||
979 | $color_male = '84beff'; |
||
980 | } |
||
981 | if (isset($params[3]) && $params[3] != '') { |
||
982 | $color_unknown = strtolower($params[3]); |
||
983 | } else { |
||
984 | $color_unknown = '777777'; |
||
985 | } |
||
986 | $sizes = explode('x', $size); |
||
987 | // Raw data - for calculation |
||
988 | $tot_f = $this->totalSexFemalesQuery(); |
||
989 | $tot_m = $this->totalSexMalesQuery(); |
||
990 | $tot_u = $this->totalSexUnknownQuery(); |
||
991 | $tot = $tot_f + $tot_m + $tot_u; |
||
992 | // I18N data - for display |
||
993 | $per_f = $this->totalSexFemalesPercentage(); |
||
994 | $per_m = $this->totalSexMalesPercentage(); |
||
995 | $per_u = $this->totalSexUnknownPercentage(); |
||
996 | if ($tot == 0) { |
||
997 | return ''; |
||
998 | } elseif ($tot_u > 0) { |
||
999 | $chd = $this->arrayToExtendedEncoding(array(4095 * $tot_u / $tot, 4095 * $tot_f / $tot, 4095 * $tot_m / $tot)); |
||
1000 | $chl = |
||
1001 | I18N::translateContext('unknown people', 'Unknown') . ' - ' . $per_u . '|' . |
||
1002 | I18N::translate('Females') . ' - ' . $per_f . '|' . |
||
1003 | I18N::translate('Males') . ' - ' . $per_m; |
||
1004 | $chart_title = |
||
1005 | I18N::translate('Males') . ' - ' . $per_m . I18N::$list_separator . |
||
1006 | I18N::translate('Females') . ' - ' . $per_f . I18N::$list_separator . |
||
1007 | I18N::translateContext('unknown people', 'Unknown') . ' - ' . $per_u; |
||
1008 | |||
1009 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_unknown},{$color_female},{$color_male}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; |
||
1010 | } else { |
||
1011 | $chd = $this->arrayToExtendedEncoding(array(4095 * $tot_f / $tot, 4095 * $tot_m / $tot)); |
||
1012 | $chl = |
||
1013 | I18N::translate('Females') . ' - ' . $per_f . '|' . |
||
1014 | I18N::translate('Males') . ' - ' . $per_m; |
||
1015 | $chart_title = I18N::translate('Males') . ' - ' . $per_m . I18N::$list_separator . |
||
1016 | I18N::translate('Females') . ' - ' . $per_f; |
||
1017 | |||
1018 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_female},{$color_male}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; |
||
1019 | } |
||
1020 | } |
||
1021 | |||
1022 | /** |
||
1023 | * Count the number of living individuals. |
||
1024 | * |
||
1025 | * The totalLiving/totalDeceased queries assume that every dead person will |
||
1026 | * have a DEAT record. It will not include individuals who were born more |
||
1027 | * than MAX_ALIVE_AGE years ago, and who have no DEAT record. |
||
1028 | * A good reason to run the “Add missing DEAT records” batch-update! |
||
1029 | * |
||
1030 | * @return int |
||
1031 | */ |
||
1032 | private function totalLivingQuery() |
||
1033 | { |
||
1034 | return (int) Database::prepare( |
||
1035 | "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'" |
||
1036 | )->execute(array( |
||
1037 | 'tree_id' => $this->tree->getTreeId(), |
||
1038 | ))->fetchOne(); |
||
1039 | } |
||
1040 | |||
1041 | /** |
||
1042 | * Count the number of living individuals. |
||
1043 | * |
||
1044 | * @return string |
||
1045 | */ |
||
1046 | public function totalLiving() |
||
1047 | { |
||
1048 | return I18N::number($this->totalLivingQuery()); |
||
1049 | } |
||
1050 | |||
1051 | /** |
||
1052 | * Count the number of living individuals. |
||
1053 | * |
||
1054 | * @return string |
||
1055 | */ |
||
1056 | public function totalLivingPercentage() |
||
1057 | { |
||
1058 | return $this->getPercentage($this->totalLivingQuery(), 'individual'); |
||
1059 | } |
||
1060 | |||
1061 | /** |
||
1062 | * Count the number of dead individuals. |
||
1063 | * |
||
1064 | * @return int |
||
1065 | */ |
||
1066 | private function totalDeceasedQuery() |
||
1067 | { |
||
1068 | return (int) Database::prepare( |
||
1069 | "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'" |
||
1070 | )->execute(array( |
||
1071 | 'tree_id' => $this->tree->getTreeId(), |
||
1072 | ))->fetchOne(); |
||
1073 | } |
||
1074 | |||
1075 | /** |
||
1076 | * Count the number of dead individuals. |
||
1077 | * |
||
1078 | * @return string |
||
1079 | */ |
||
1080 | public function totalDeceased() |
||
1081 | { |
||
1082 | return I18N::number($this->totalDeceasedQuery()); |
||
1083 | } |
||
1084 | |||
1085 | /** |
||
1086 | * Count the number of dead individuals. |
||
1087 | * |
||
1088 | * @return string |
||
1089 | */ |
||
1090 | public function totalDeceasedPercentage() |
||
1091 | { |
||
1092 | return $this->getPercentage($this->totalDeceasedQuery(), 'individual'); |
||
1093 | } |
||
1094 | |||
1095 | /** |
||
1096 | * Create a chart showing mortality. |
||
1097 | * |
||
1098 | * @param string[] $params |
||
1099 | * |
||
1100 | * @return string |
||
1101 | */ |
||
1102 | public function chartMortality($params = array()) |
||
1103 | { |
||
1104 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
1105 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
1106 | |||
1107 | if (isset($params[0]) && $params[0] != '') { |
||
1108 | $size = strtolower($params[0]); |
||
1109 | } else { |
||
1110 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
1111 | } |
||
1112 | if (isset($params[1]) && $params[1] != '') { |
||
1113 | $color_living = strtolower($params[1]); |
||
1114 | } else { |
||
1115 | $color_living = 'ffffff'; |
||
1116 | } |
||
1117 | if (isset($params[2]) && $params[2] != '') { |
||
1118 | $color_dead = strtolower($params[2]); |
||
1119 | } else { |
||
1120 | $color_dead = 'cccccc'; |
||
1121 | } |
||
1122 | $sizes = explode('x', $size); |
||
1123 | // Raw data - for calculation |
||
1124 | $tot_l = $this->totalLivingQuery(); |
||
1125 | $tot_d = $this->totalDeceasedQuery(); |
||
1126 | $tot = $tot_l + $tot_d; |
||
1127 | // I18N data - for display |
||
1128 | $per_l = $this->totalLivingPercentage(); |
||
1129 | $per_d = $this->totalDeceasedPercentage(); |
||
1130 | if ($tot == 0) { |
||
1131 | return ''; |
||
1132 | } else { |
||
1133 | $chd = $this->arrayToExtendedEncoding(array(4095 * $tot_l / $tot, 4095 * $tot_d / $tot)); |
||
1134 | $chl = |
||
1135 | I18N::translate('Living') . ' - ' . $per_l . '|' . |
||
1136 | I18N::translate('Dead') . ' - ' . $per_d . '|'; |
||
1137 | $chart_title = I18N::translate('Living') . ' - ' . $per_l . I18N::$list_separator . |
||
1138 | I18N::translate('Dead') . ' - ' . $per_d; |
||
1139 | |||
1140 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_living},{$color_dead}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; |
||
1141 | } |
||
1142 | } |
||
1143 | |||
1144 | /** |
||
1145 | * Count the number of users. |
||
1146 | * |
||
1147 | * @param string[] $params |
||
1148 | * |
||
1149 | * @return string |
||
1150 | */ |
||
1151 | public function totalUsers($params = array()) |
||
1152 | { |
||
1153 | if (isset($params[0])) { |
||
1154 | $total = count(User::all()) + (int) $params[0]; |
||
1155 | } else { |
||
1156 | $total = count(User::all()); |
||
1157 | } |
||
1158 | |||
1159 | return I18N::number($total); |
||
1160 | } |
||
1161 | |||
1162 | /** |
||
1163 | * Count the number of administrators. |
||
1164 | * |
||
1165 | * @return string |
||
1166 | */ |
||
1167 | public function totalAdmins() |
||
1168 | { |
||
1169 | return I18N::number(count(User::allAdmins())); |
||
1170 | } |
||
1171 | |||
1172 | /** |
||
1173 | * Count the number of administrators. |
||
1174 | * |
||
1175 | * @return string |
||
1176 | */ |
||
1177 | public function totalNonAdmins() |
||
1178 | { |
||
1179 | return I18N::number(count(User::all()) - count(User::allAdmins())); |
||
1180 | } |
||
1181 | |||
1182 | /** |
||
1183 | * Count the number of media records with a given type. |
||
1184 | * |
||
1185 | * @param string $type |
||
1186 | * |
||
1187 | * @return int |
||
1188 | */ |
||
1189 | private function totalMediaType($type = 'all') |
||
1190 | { |
||
1191 | if (!in_array($type, $this->_media_types) && $type != 'all' && $type != 'unknown') { |
||
1192 | return 0; |
||
1193 | } |
||
1194 | $sql = "SELECT COUNT(*) AS tot FROM `##media` WHERE m_file=?"; |
||
1195 | $vars = array($this->tree->getTreeId()); |
||
1196 | |||
1197 | if ($type != 'all') { |
||
1198 | if ($type == 'unknown') { |
||
1199 | // There has to be a better way then this :( |
||
1200 | foreach ($this->_media_types as $t) { |
||
1201 | $sql .= " AND (m_gedcom NOT LIKE ? AND m_gedcom NOT LIKE ?)"; |
||
1202 | $vars[] = "%3 TYPE {$t}%"; |
||
1203 | $vars[] = "%1 _TYPE {$t}%"; |
||
1204 | } |
||
1205 | } else { |
||
1206 | $sql .= " AND (m_gedcom LIKE ? OR m_gedcom LIKE ?)"; |
||
1207 | $vars[] = "%3 TYPE {$type}%"; |
||
1208 | $vars[] = "%1 _TYPE {$type}%"; |
||
1209 | } |
||
1210 | } |
||
1211 | |||
1212 | return (int) Database::prepare($sql)->execute($vars)->fetchOne(); |
||
1213 | } |
||
1214 | |||
1215 | /** |
||
1216 | * Count the number of media records. |
||
1217 | * |
||
1218 | * @return string |
||
1219 | */ |
||
1220 | public function totalMedia() |
||
1221 | { |
||
1222 | return I18N::number($this->totalMediaType('all')); |
||
1223 | } |
||
1224 | |||
1225 | /** |
||
1226 | * Count the number of media records with type "audio". |
||
1227 | * |
||
1228 | * @return string |
||
1229 | */ |
||
1230 | public function totalMediaAudio() |
||
1231 | { |
||
1232 | return I18N::number($this->totalMediaType('audio')); |
||
1233 | } |
||
1234 | |||
1235 | /** |
||
1236 | * Count the number of media records with type "book". |
||
1237 | * |
||
1238 | * @return string |
||
1239 | */ |
||
1240 | public function totalMediaBook() |
||
1241 | { |
||
1242 | return I18N::number($this->totalMediaType('book')); |
||
1243 | } |
||
1244 | |||
1245 | /** |
||
1246 | * Count the number of media records with type "card". |
||
1247 | * |
||
1248 | * @return string |
||
1249 | */ |
||
1250 | public function totalMediaCard() |
||
1251 | { |
||
1252 | return I18N::number($this->totalMediaType('card')); |
||
1253 | } |
||
1254 | |||
1255 | /** |
||
1256 | * Count the number of media records with type "certificate". |
||
1257 | * |
||
1258 | * @return string |
||
1259 | */ |
||
1260 | public function totalMediaCertificate() |
||
1261 | { |
||
1262 | return I18N::number($this->totalMediaType('certificate')); |
||
1263 | } |
||
1264 | |||
1265 | /** |
||
1266 | * Count the number of media records with type "coat of arms". |
||
1267 | * |
||
1268 | * @return string |
||
1269 | */ |
||
1270 | public function totalMediaCoatOfArms() |
||
1271 | { |
||
1272 | return I18N::number($this->totalMediaType('coat')); |
||
1273 | } |
||
1274 | |||
1275 | /** |
||
1276 | * Count the number of media records with type "document". |
||
1277 | * |
||
1278 | * @return string |
||
1279 | */ |
||
1280 | public function totalMediaDocument() |
||
1281 | { |
||
1282 | return I18N::number($this->totalMediaType('document')); |
||
1283 | } |
||
1284 | |||
1285 | /** |
||
1286 | * Count the number of media records with type "electronic". |
||
1287 | * |
||
1288 | * @return string |
||
1289 | */ |
||
1290 | public function totalMediaElectronic() |
||
1291 | { |
||
1292 | return I18N::number($this->totalMediaType('electronic')); |
||
1293 | } |
||
1294 | |||
1295 | /** |
||
1296 | * Count the number of media records with type "magazine". |
||
1297 | * |
||
1298 | * @return string |
||
1299 | */ |
||
1300 | public function totalMediaMagazine() |
||
1301 | { |
||
1302 | return I18N::number($this->totalMediaType('magazine')); |
||
1303 | } |
||
1304 | |||
1305 | /** |
||
1306 | * Count the number of media records with type "manuscript". |
||
1307 | * |
||
1308 | * @return string |
||
1309 | */ |
||
1310 | public function totalMediaManuscript() |
||
1311 | { |
||
1312 | return I18N::number($this->totalMediaType('manuscript')); |
||
1313 | } |
||
1314 | |||
1315 | /** |
||
1316 | * Count the number of media records with type "map". |
||
1317 | * |
||
1318 | * @return string |
||
1319 | */ |
||
1320 | public function totalMediaMap() |
||
1321 | { |
||
1322 | return I18N::number($this->totalMediaType('map')); |
||
1323 | } |
||
1324 | |||
1325 | /** |
||
1326 | * Count the number of media records with type "microfiche". |
||
1327 | * |
||
1328 | * @return string |
||
1329 | */ |
||
1330 | public function totalMediaFiche() |
||
1331 | { |
||
1332 | return I18N::number($this->totalMediaType('fiche')); |
||
1333 | } |
||
1334 | |||
1335 | /** |
||
1336 | * Count the number of media records with type "microfilm". |
||
1337 | * |
||
1338 | * @return string |
||
1339 | */ |
||
1340 | public function totalMediaFilm() |
||
1341 | { |
||
1342 | return I18N::number($this->totalMediaType('film')); |
||
1343 | } |
||
1344 | |||
1345 | /** |
||
1346 | * Count the number of media records with type "newspaper". |
||
1347 | * |
||
1348 | * @return string |
||
1349 | */ |
||
1350 | public function totalMediaNewspaper() |
||
1351 | { |
||
1352 | return I18N::number($this->totalMediaType('newspaper')); |
||
1353 | } |
||
1354 | |||
1355 | /** |
||
1356 | * Count the number of media records with type "painting". |
||
1357 | * |
||
1358 | * @return string |
||
1359 | */ |
||
1360 | public function totalMediaPainting() |
||
1361 | { |
||
1362 | return I18N::number($this->totalMediaType('painting')); |
||
1363 | } |
||
1364 | |||
1365 | /** |
||
1366 | * Count the number of media records with type "photograph". |
||
1367 | * |
||
1368 | * @return string |
||
1369 | */ |
||
1370 | public function totalMediaPhoto() |
||
1371 | { |
||
1372 | return I18N::number($this->totalMediaType('photo')); |
||
1373 | } |
||
1374 | |||
1375 | /** |
||
1376 | * Count the number of media records with type "tombstone". |
||
1377 | * |
||
1378 | * @return string |
||
1379 | */ |
||
1380 | public function totalMediaTombstone() |
||
1381 | { |
||
1382 | return I18N::number($this->totalMediaType('tombstone')); |
||
1383 | } |
||
1384 | |||
1385 | /** |
||
1386 | * Count the number of media records with type "video". |
||
1387 | * |
||
1388 | * @return string |
||
1389 | */ |
||
1390 | public function totalMediaVideo() |
||
1391 | { |
||
1392 | return I18N::number($this->totalMediaType('video')); |
||
1393 | } |
||
1394 | |||
1395 | /** |
||
1396 | * Count the number of media records with type "other". |
||
1397 | * |
||
1398 | * @return string |
||
1399 | */ |
||
1400 | public function totalMediaOther() |
||
1401 | { |
||
1402 | return I18N::number($this->totalMediaType('other')); |
||
1403 | } |
||
1404 | |||
1405 | /** |
||
1406 | * Count the number of media records with type "unknown". |
||
1407 | * |
||
1408 | * @return string |
||
1409 | */ |
||
1410 | public function totalMediaUnknown() |
||
1411 | { |
||
1412 | return I18N::number($this->totalMediaType('unknown')); |
||
1413 | } |
||
1414 | |||
1415 | /** |
||
1416 | * Create a chart of media types. |
||
1417 | * |
||
1418 | * @param string[] $params |
||
1419 | * |
||
1420 | * @return string |
||
1421 | */ |
||
1422 | public function chartMedia($params = array()) |
||
1423 | { |
||
1424 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
1425 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
1426 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
1427 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
1428 | |||
1429 | if (isset($params[0]) && $params[0] != '') { |
||
1430 | $size = strtolower($params[0]); |
||
1431 | } else { |
||
1432 | $size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y; |
||
1433 | } |
||
1434 | if (isset($params[1]) && $params[1] != '') { |
||
1435 | $color_from = strtolower($params[1]); |
||
1436 | } else { |
||
1437 | $color_from = $WT_STATS_CHART_COLOR1; |
||
1438 | } |
||
1439 | if (isset($params[2]) && $params[2] != '') { |
||
1440 | $color_to = strtolower($params[2]); |
||
1441 | } else { |
||
1442 | $color_to = $WT_STATS_CHART_COLOR2; |
||
1443 | } |
||
1444 | $sizes = explode('x', $size); |
||
1445 | $tot = $this->totalMediaType('all'); |
||
1446 | // Beware divide by zero |
||
1447 | if ($tot == 0) { |
||
1448 | return I18N::translate('None'); |
||
1449 | } |
||
1450 | // Build a table listing only the media types actually present in the GEDCOM |
||
1451 | $mediaCounts = array(); |
||
1452 | $mediaTypes = ""; |
||
1453 | $chart_title = ""; |
||
1454 | $c = 0; |
||
1455 | $max = 0; |
||
1456 | $media = array(); |
||
1457 | foreach ($this->_media_types as $type) { |
||
1458 | $count = $this->totalMediaType($type); |
||
1459 | if ($count > 0) { |
||
1460 | $media[$type] = $count; |
||
1461 | if ($count > $max) { |
||
1462 | $max = $count; |
||
1463 | } |
||
1464 | $c += $count; |
||
1465 | } |
||
1466 | } |
||
1467 | $count = $this->totalMediaType('unknown'); |
||
1468 | if ($count > 0) { |
||
1469 | $media['unknown'] = $tot - $c; |
||
1470 | if ($tot - $c > $max) { |
||
1471 | $max = $count; |
||
1472 | } |
||
1473 | } |
||
1474 | if (($max / $tot) > 0.6 && count($media) > 10) { |
||
1475 | arsort($media); |
||
1476 | $media = array_slice($media, 0, 10); |
||
1477 | $c = $tot; |
||
1478 | foreach ($media as $cm) { |
||
1479 | $c -= $cm; |
||
1480 | } |
||
1481 | if (isset($media['other'])) { |
||
1482 | $media['other'] += $c; |
||
1483 | } else { |
||
1484 | $media['other'] = $c; |
||
1485 | } |
||
1486 | } |
||
1487 | asort($media); |
||
1488 | foreach ($media as $type => $count) { |
||
1489 | $mediaCounts[] = round(100 * $count / $tot, 0); |
||
1490 | $mediaTypes .= GedcomTag::getFileFormTypeValue($type) . ' - ' . I18N::number($count) . '|'; |
||
1491 | $chart_title .= GedcomTag::getFileFormTypeValue($type) . ' (' . $count . '), '; |
||
1492 | } |
||
1493 | $chart_title = substr($chart_title, 0, -2); |
||
1494 | $chd = $this->arrayToExtendedEncoding($mediaCounts); |
||
1495 | $chl = substr($mediaTypes, 0, -1); |
||
1496 | |||
1497 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; |
||
1498 | } |
||
1499 | |||
1500 | /** |
||
1501 | * Birth and Death |
||
1502 | * |
||
1503 | * @param string $type |
||
1504 | * @param string $life_dir |
||
1505 | * @param string $birth_death |
||
1506 | * |
||
1507 | * @return string |
||
1508 | */ |
||
1509 | private function mortalityQuery($type = 'full', $life_dir = 'ASC', $birth_death = 'BIRT') |
||
1510 | { |
||
1511 | if ($birth_death == 'MARR') { |
||
1512 | $query_field = "'MARR'"; |
||
1513 | } elseif ($birth_death == 'DIV') { |
||
1514 | $query_field = "'DIV'"; |
||
1515 | } elseif ($birth_death == 'BIRT') { |
||
1516 | $query_field = "'BIRT'"; |
||
1517 | } else { |
||
1518 | $query_field = "'DEAT'"; |
||
1519 | } |
||
1520 | if ($life_dir == 'ASC') { |
||
1521 | $dmod = 'MIN'; |
||
1522 | } else { |
||
1523 | $dmod = 'MAX'; |
||
1524 | } |
||
1525 | $rows = $this->runSql( |
||
1526 | "SELECT d_year, d_type, d_fact, d_gid" . |
||
1527 | " FROM `##dates`" . |
||
1528 | " WHERE d_file={$this->tree->getTreeId()} AND d_fact IN ({$query_field}) AND d_julianday1=(" . |
||
1529 | " SELECT {$dmod}( d_julianday1 )" . |
||
1530 | " FROM `##dates`" . |
||
1531 | " WHERE d_file={$this->tree->getTreeId()} AND d_fact IN ({$query_field}) AND d_julianday1<>0 )" . |
||
1532 | " LIMIT 1" |
||
1533 | ); |
||
1534 | if (!isset($rows[0])) { |
||
1535 | return ''; |
||
1536 | } |
||
1537 | $row = $rows[0]; |
||
1538 | $record = GedcomRecord::getInstance($row['d_gid'], $this->tree); |
||
1539 | switch ($type) { |
||
1540 | default: |
||
1541 | case 'full': |
||
1542 | if ($record->canShow()) { |
||
1543 | $result = $record->formatList('span', false, $record->getFullName()); |
||
1544 | } else { |
||
1545 | $result = I18N::translate('This information is private and cannot be shown.'); |
||
1546 | } |
||
1547 | break; |
||
1548 | case 'year': |
||
1549 | $date = new Date($row['d_type'] . ' ' . $row['d_year']); |
||
1550 | $result = $date->display(); |
||
1551 | break; |
||
1552 | case 'name': |
||
1553 | $result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>"; |
||
1554 | break; |
||
1555 | case 'place': |
||
1556 | $fact = GedcomRecord::getInstance($row['d_gid'], $this->tree)->getFirstFact($row['d_fact']); |
||
1557 | if ($fact) { |
||
1558 | $result = FunctionsPrint::formatFactPlace($fact, true, true, true); |
||
1559 | } else { |
||
1560 | $result = I18N::translate('Private'); |
||
1561 | } |
||
1562 | break; |
||
1563 | } |
||
1564 | |||
1565 | return $result; |
||
1566 | } |
||
1567 | |||
1568 | /** |
||
1569 | * Places |
||
1570 | * |
||
1571 | * @param string $what |
||
1572 | * @param string $fact |
||
1573 | * @param int $parent |
||
1574 | * @param bool $country |
||
1575 | * |
||
1576 | * @return int[]|string[][] |
||
1577 | */ |
||
1578 | public function statsPlaces($what = 'ALL', $fact = '', $parent = 0, $country = false) |
||
1579 | { |
||
1580 | if ($fact) { |
||
1581 | if ($what == 'INDI') { |
||
1582 | $rows = Database::prepare( |
||
1583 | "SELECT i_gedcom AS ged FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom LIKE '%\n2 PLAC %'" |
||
1584 | )->execute(array( |
||
1585 | 'tree_id' => $this->tree->getTreeId(), |
||
1586 | ))->fetchAll(); |
||
1587 | } elseif ($what == 'FAM') { |
||
1588 | $rows = Database::prepare( |
||
1589 | "SELECT f_gedcom AS ged FROM `##families` WHERE f_file = :tree_id AND f_gedcom LIKE '%\n2 PLAC %'" |
||
1590 | )->execute(array( |
||
1591 | 'tree_id' => $this->tree->getTreeId(), |
||
1592 | ))->fetchAll(); |
||
1593 | } |
||
1594 | $placelist = array(); |
||
1595 | foreach ($rows as $row) { |
||
1596 | if (preg_match('/\n1 ' . $fact . '(?:\n[2-9].*)*\n2 PLAC (.+)/', $row->ged, $match)) { |
||
1597 | if ($country) { |
||
1598 | $tmp = explode(Place::GEDCOM_SEPARATOR, $match[1]); |
||
1599 | $place = end($tmp); |
||
1600 | } else { |
||
1601 | $place = $match[1]; |
||
1602 | } |
||
1603 | if (!isset($placelist[$place])) { |
||
1604 | $placelist[$place] = 1; |
||
1605 | } else { |
||
1606 | $placelist[$place]++; |
||
1607 | } |
||
1608 | } |
||
1609 | } |
||
1610 | |||
1611 | return $placelist; |
||
1612 | } elseif ($parent > 0) { |
||
1613 | // used by placehierarchy googlemap module |
||
1614 | if ($what == 'INDI') { |
||
1615 | $join = " JOIN `##individuals` ON pl_file = i_file AND pl_gid = i_id"; |
||
1616 | } elseif ($what == 'FAM') { |
||
1617 | $join = " JOIN `##families` ON pl_file = f_file AND pl_gid = f_id"; |
||
1618 | } else { |
||
1619 | $join = ""; |
||
1620 | } |
||
1621 | $rows = $this->runSql( |
||
1622 | " SELECT" . |
||
1623 | " p_place AS place," . |
||
1624 | " COUNT(*) AS tot" . |
||
1625 | " FROM" . |
||
1626 | " `##places`" . |
||
1627 | " JOIN `##placelinks` ON pl_file=p_file AND p_id=pl_p_id" . |
||
1628 | $join . |
||
1629 | " WHERE" . |
||
1630 | " p_id={$parent} AND" . |
||
1631 | " p_file={$this->tree->getTreeId()}" . |
||
1632 | " GROUP BY place" |
||
1633 | ); |
||
1634 | |||
1635 | return $rows; |
||
1636 | } else { |
||
1637 | if ($what == 'INDI') { |
||
1638 | $join = " JOIN `##individuals` ON pl_file = i_file AND pl_gid = i_id"; |
||
1639 | } elseif ($what == 'FAM') { |
||
1640 | $join = " JOIN `##families` ON pl_file = f_file AND pl_gid = f_id"; |
||
1641 | } else { |
||
1642 | $join = ""; |
||
1643 | } |
||
1644 | $rows = $this->runSql( |
||
1645 | " SELECT" . |
||
1646 | " p_place AS country," . |
||
1647 | " COUNT(*) AS tot" . |
||
1648 | " FROM" . |
||
1649 | " `##places`" . |
||
1650 | " JOIN `##placelinks` ON pl_file=p_file AND p_id=pl_p_id" . |
||
1651 | $join . |
||
1652 | " WHERE" . |
||
1653 | " p_file={$this->tree->getTreeId()}" . |
||
1654 | " AND p_parent_id='0'" . |
||
1655 | " GROUP BY country ORDER BY tot DESC, country ASC" |
||
1656 | ); |
||
1657 | |||
1658 | return $rows; |
||
1659 | } |
||
1660 | } |
||
1661 | |||
1662 | /** |
||
1663 | * Count total places. |
||
1664 | * |
||
1665 | * @return int |
||
1666 | */ |
||
1667 | private function totalPlacesQuery() |
||
1668 | { |
||
1669 | return |
||
1670 | (int) Database::prepare("SELECT COUNT(*) FROM `##places` WHERE p_file=?") |
||
1671 | ->execute(array($this->tree->getTreeId())) |
||
1672 | ->fetchOne(); |
||
1673 | } |
||
1674 | |||
1675 | /** |
||
1676 | * Count total places. |
||
1677 | * |
||
1678 | * @return string |
||
1679 | */ |
||
1680 | public function totalPlaces() |
||
1681 | { |
||
1682 | return I18N::number($this->totalPlacesQuery()); |
||
1683 | } |
||
1684 | |||
1685 | /** |
||
1686 | * Create a chart showing where events occurred. |
||
1687 | * |
||
1688 | * @param string[] $params |
||
1689 | * |
||
1690 | * @return string |
||
1691 | */ |
||
1692 | public function chartDistribution($params = array()) |
||
1693 | { |
||
1694 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
1695 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
1696 | $WT_STATS_CHART_COLOR3 = Theme::theme()->parameter('distribution-chart-low-values'); |
||
1697 | $WT_STATS_MAP_X = Theme::theme()->parameter('distribution-chart-x'); |
||
1698 | $WT_STATS_MAP_Y = Theme::theme()->parameter('distribution-chart-y'); |
||
1699 | |||
1700 | if (isset($params[0])) { |
||
1701 | $chart_shows = $params[0]; |
||
1702 | } else { |
||
1703 | $chart_shows = 'world'; |
||
1704 | } |
||
1705 | if (isset($params[1])) { |
||
1706 | $chart_type = $params[1]; |
||
1707 | } else { |
||
1708 | $chart_type = ''; |
||
1709 | } |
||
1710 | if (isset($params[2])) { |
||
1711 | $surname = $params[2]; |
||
1712 | } else { |
||
1713 | $surname = ''; |
||
1714 | } |
||
1715 | |||
1716 | if ($this->totalPlacesQuery() == 0) { |
||
1717 | return ''; |
||
1718 | } |
||
1719 | // Get the country names for each language |
||
1720 | $country_to_iso3166 = array(); |
||
1721 | foreach (I18N::activeLocales() as $locale) { |
||
1722 | I18N::init($locale->languageTag()); |
||
1723 | $countries = $this->getAllCountries(); |
||
1724 | foreach ($this->iso3166() as $three => $two) { |
||
1725 | $country_to_iso3166[$three] = $two; |
||
1726 | $country_to_iso3166[$countries[$three]] = $two; |
||
1727 | } |
||
1728 | } |
||
1729 | I18N::init(WT_LOCALE); |
||
1730 | switch ($chart_type) { |
||
1731 | case 'surname_distribution_chart': |
||
1732 | if ($surname == "") { |
||
1733 | $surname = $this->getCommonSurname(); |
||
1734 | } |
||
1735 | $chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname; |
||
1736 | // Count how many people are events in each country |
||
1737 | $surn_countries = array(); |
||
1738 | $indis = QueryName::individuals($this->tree, I18N::strtoupper($surname), '', '', false, false); |
||
1739 | foreach ($indis as $person) { |
||
1740 | if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcom(), $matches)) { |
||
1741 | // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. |
||
1742 | foreach ($matches[1] as $country) { |
||
1743 | if (array_key_exists($country, $country_to_iso3166)) { |
||
1744 | if (array_key_exists($country_to_iso3166[$country], $surn_countries)) { |
||
1745 | $surn_countries[$country_to_iso3166[$country]]++; |
||
1746 | } else { |
||
1747 | $surn_countries[$country_to_iso3166[$country]] = 1; |
||
1748 | } |
||
1749 | } |
||
1750 | } |
||
1751 | } |
||
1752 | }; |
||
1753 | break; |
||
1754 | case 'birth_distribution_chart': |
||
1755 | $chart_title = I18N::translate('Birth by country'); |
||
1756 | // Count how many people were born in each country |
||
1757 | $surn_countries = array(); |
||
1758 | $b_countries = $this->statsPlaces('INDI', 'BIRT', 0, true); |
||
1759 | foreach ($b_countries as $place => $count) { |
||
1760 | $country = $place; |
||
1761 | if (array_key_exists($country, $country_to_iso3166)) { |
||
1762 | if (!isset($surn_countries[$country_to_iso3166[$country]])) { |
||
1763 | $surn_countries[$country_to_iso3166[$country]] = $count; |
||
1764 | } else { |
||
1765 | $surn_countries[$country_to_iso3166[$country]] += $count; |
||
1766 | } |
||
1767 | } |
||
1768 | } |
||
1769 | break; |
||
1770 | case 'death_distribution_chart': |
||
1771 | $chart_title = I18N::translate('Death by country'); |
||
1772 | // Count how many people were death in each country |
||
1773 | $surn_countries = array(); |
||
1774 | $d_countries = $this->statsPlaces('INDI', 'DEAT', 0, true); |
||
1775 | foreach ($d_countries as $place => $count) { |
||
1776 | $country = $place; |
||
1777 | if (array_key_exists($country, $country_to_iso3166)) { |
||
1778 | if (!isset($surn_countries[$country_to_iso3166[$country]])) { |
||
1779 | $surn_countries[$country_to_iso3166[$country]] = $count; |
||
1780 | } else { |
||
1781 | $surn_countries[$country_to_iso3166[$country]] += $count; |
||
1782 | } |
||
1783 | } |
||
1784 | } |
||
1785 | break; |
||
1786 | case 'marriage_distribution_chart': |
||
1787 | $chart_title = I18N::translate('Marriage by country'); |
||
1788 | // Count how many families got marriage in each country |
||
1789 | $surn_countries = array(); |
||
1790 | $m_countries = $this->statsPlaces('FAM'); |
||
1791 | // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. |
||
1792 | foreach ($m_countries as $place) { |
||
1793 | $country = $place['country']; |
||
1794 | if (array_key_exists($country, $country_to_iso3166)) { |
||
1795 | if (!isset($surn_countries[$country_to_iso3166[$country]])) { |
||
1796 | $surn_countries[$country_to_iso3166[$country]] = $place['tot']; |
||
1797 | } else { |
||
1798 | $surn_countries[$country_to_iso3166[$country]] += $place['tot']; |
||
1799 | } |
||
1800 | } |
||
1801 | } |
||
1802 | break; |
||
1803 | case 'indi_distribution_chart': |
||
1804 | default: |
||
1805 | $chart_title = I18N::translate('Individual distribution chart'); |
||
1806 | // Count how many people have events in each country |
||
1807 | $surn_countries = array(); |
||
1808 | $a_countries = $this->statsPlaces('INDI'); |
||
1809 | // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. |
||
1810 | foreach ($a_countries as $place) { |
||
1811 | $country = $place['country']; |
||
1812 | if (array_key_exists($country, $country_to_iso3166)) { |
||
1813 | if (!isset($surn_countries[$country_to_iso3166[$country]])) { |
||
1814 | $surn_countries[$country_to_iso3166[$country]] = $place['tot']; |
||
1815 | } else { |
||
1816 | $surn_countries[$country_to_iso3166[$country]] += $place['tot']; |
||
1817 | } |
||
1818 | } |
||
1819 | } |
||
1820 | break; |
||
1821 | } |
||
1822 | $chart_url = "https://chart.googleapis.com/chart?cht=t&chtm=" . $chart_shows; |
||
1823 | $chart_url .= "&chco=" . $WT_STATS_CHART_COLOR1 . "," . $WT_STATS_CHART_COLOR3 . "," . $WT_STATS_CHART_COLOR2; // country colours |
||
1824 | $chart_url .= "&chf=bg,s,ECF5FF"; // sea colour |
||
1825 | $chart_url .= "&chs=" . $WT_STATS_MAP_X . "x" . $WT_STATS_MAP_Y; |
||
1826 | $chart_url .= "&chld=" . implode('', array_keys($surn_countries)) . "&chd=s:"; |
||
1827 | foreach ($surn_countries as $count) { |
||
1828 | $chart_url .= substr(WT_GOOGLE_CHART_ENCODING, (int) ($count / max($surn_countries) * 61), 1); |
||
1829 | } |
||
1830 | $chart = '<div id="google_charts" class="center">'; |
||
1831 | $chart .= '<p>' . $chart_title . '</p>'; |
||
1832 | $chart .= '<div><img src="' . $chart_url . '" alt="' . $chart_title . '" title="' . $chart_title . '" class="gchart" /><br>'; |
||
1833 | $chart .= '<table class="center"><tr>'; |
||
1834 | $chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR2 . '" width="12"></td><td>' . I18N::translate('Highest population') . '</td>'; |
||
1835 | $chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR3 . '" width="12"></td><td>' . I18N::translate('Lowest population') . '</td>'; |
||
1836 | $chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR1 . '" width="12"></td><td>' . I18N::translate('Nobody at all') . '</td>'; |
||
1837 | $chart .= '</tr></table></div></div>'; |
||
1838 | |||
1839 | return $chart; |
||
1840 | } |
||
1841 | |||
1842 | /** |
||
1843 | * A list of common countries. |
||
1844 | * |
||
1845 | * @return string |
||
1846 | */ |
||
1847 | public function commonCountriesList() |
||
1848 | { |
||
1849 | $countries = $this->statsPlaces(); |
||
1850 | if (empty($countries)) { |
||
1851 | return ''; |
||
1852 | } |
||
1853 | $top10 = array(); |
||
1854 | $i = 1; |
||
1855 | // Get the country names for each language |
||
1856 | $country_names = array(); |
||
1857 | foreach (I18N::activeLocales() as $locale) { |
||
1858 | I18N::init($locale->languageTag()); |
||
1859 | $all_countries = $this->getAllCountries(); |
||
1860 | foreach ($all_countries as $country_code => $country_name) { |
||
1861 | $country_names[$country_name] = $country_code; |
||
1862 | } |
||
1863 | } |
||
1864 | I18N::init(WT_LOCALE); |
||
1865 | $all_db_countries = array(); |
||
1866 | foreach ($countries as $place) { |
||
1867 | $country = trim($place['country']); |
||
1868 | if (array_key_exists($country, $country_names)) { |
||
1869 | if (!isset($all_db_countries[$country_names[$country]][$country])) { |
||
1870 | $all_db_countries[$country_names[$country]][$country] = (int) $place['tot']; |
||
1871 | } else { |
||
1872 | $all_db_countries[$country_names[$country]][$country] += (int) $place['tot']; |
||
1873 | } |
||
1874 | } |
||
1875 | } |
||
1876 | // get all the user’s countries names |
||
1877 | $all_countries = $this->getAllCountries(); |
||
1878 | foreach ($all_db_countries as $country_code => $country) { |
||
1879 | $top10[] = '<li>'; |
||
1880 | foreach ($country as $country_name => $tot) { |
||
1881 | $tmp = new Place($country_name, $this->tree); |
||
1882 | $place = '<a href="' . $tmp->getURL() . '" class="list_item">' . $all_countries[$country_code] . '</a>'; |
||
1883 | $top10[] .= $place . ' - ' . I18N::number($tot); |
||
1884 | } |
||
1885 | $top10[] .= '</li>'; |
||
1886 | if ($i++ == 10) { |
||
1887 | break; |
||
1888 | } |
||
1889 | } |
||
1890 | $top10 = implode('', $top10); |
||
1891 | |||
1892 | return '<ul>' . $top10 . '</ul>'; |
||
1893 | } |
||
1894 | |||
1895 | /** |
||
1896 | * A list of common birth places. |
||
1897 | * |
||
1898 | * @return string |
||
1899 | */ |
||
1900 | public function commonBirthPlacesList() |
||
1901 | { |
||
1902 | $places = $this->statsPlaces('INDI', 'BIRT'); |
||
1903 | $top10 = array(); |
||
1904 | $i = 1; |
||
1905 | arsort($places); |
||
1906 | foreach ($places as $place => $count) { |
||
1907 | $tmp = new Place($place, $this->tree); |
||
1908 | $place = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>'; |
||
1909 | $top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>'; |
||
1910 | if ($i++ == 10) { |
||
1911 | break; |
||
1912 | } |
||
1913 | } |
||
1914 | $top10 = implode('', $top10); |
||
1915 | |||
1916 | return '<ul>' . $top10 . '</ul>'; |
||
1917 | } |
||
1918 | |||
1919 | /** |
||
1920 | * A list of common death places. |
||
1921 | * |
||
1922 | * @return string |
||
1923 | */ |
||
1924 | public function commonDeathPlacesList() |
||
1925 | { |
||
1926 | $places = $this->statsPlaces('INDI', 'DEAT'); |
||
1927 | $top10 = array(); |
||
1928 | $i = 1; |
||
1929 | arsort($places); |
||
1930 | foreach ($places as $place => $count) { |
||
1931 | $tmp = new Place($place, $this->tree); |
||
1932 | $place = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>'; |
||
1933 | $top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>'; |
||
1934 | if ($i++ == 10) { |
||
1935 | break; |
||
1936 | } |
||
1937 | } |
||
1938 | $top10 = implode('', $top10); |
||
1939 | |||
1940 | return '<ul>' . $top10 . '</ul>'; |
||
1941 | } |
||
1942 | |||
1943 | /** |
||
1944 | * A list of common marriage places. |
||
1945 | * |
||
1946 | * @return string |
||
1947 | */ |
||
1948 | public function commonMarriagePlacesList() |
||
1949 | { |
||
1950 | $places = $this->statsPlaces('FAM', 'MARR'); |
||
1951 | $top10 = array(); |
||
1952 | $i = 1; |
||
1953 | arsort($places); |
||
1954 | foreach ($places as $place => $count) { |
||
1955 | $tmp = new Place($place, $this->tree); |
||
1956 | $place = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>'; |
||
1957 | $top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>'; |
||
1958 | if ($i++ == 10) { |
||
1959 | break; |
||
1960 | } |
||
1961 | } |
||
1962 | $top10 = implode('', $top10); |
||
1963 | |||
1964 | return '<ul>' . $top10 . '</ul>'; |
||
1965 | } |
||
1966 | |||
1967 | /** |
||
1968 | * Create a chart of birth places. |
||
1969 | * |
||
1970 | * @param bool $simple |
||
1971 | * @param bool $sex |
||
1972 | * @param int $year1 |
||
1973 | * @param int $year2 |
||
1974 | * @param string[] $params |
||
1975 | * |
||
1976 | * @return array|string |
||
1977 | */ |
||
1978 | public function statsBirthQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) |
||
1979 | { |
||
1980 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
1981 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
1982 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
1983 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
1984 | |||
1985 | if ($simple) { |
||
1986 | $sql = |
||
1987 | "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total FROM `##dates` " . |
||
1988 | "WHERE " . |
||
1989 | "d_file={$this->tree->getTreeId()} AND " . |
||
1990 | "d_year<>0 AND " . |
||
1991 | "d_fact='BIRT' AND " . |
||
1992 | "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
1993 | } elseif ($sex) { |
||
1994 | $sql = |
||
1995 | "SELECT d_month, i_sex, COUNT(*) AS total FROM `##dates` " . |
||
1996 | "JOIN `##individuals` ON d_file = i_file AND d_gid = i_id " . |
||
1997 | "WHERE " . |
||
1998 | "d_file={$this->tree->getTreeId()} AND " . |
||
1999 | "d_fact='BIRT' AND " . |
||
2000 | "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
2001 | } else { |
||
2002 | $sql = |
||
2003 | "SELECT d_month, COUNT(*) AS total FROM `##dates` " . |
||
2004 | "WHERE " . |
||
2005 | "d_file={$this->tree->getTreeId()} AND " . |
||
2006 | "d_fact='BIRT' AND " . |
||
2007 | "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
2008 | } |
||
2009 | if ($year1 >= 0 && $year2 >= 0) { |
||
2010 | $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
2011 | } |
||
2012 | if ($simple) { |
||
2013 | $sql .= " GROUP BY century ORDER BY century"; |
||
2014 | } else { |
||
2015 | $sql .= " GROUP BY d_month"; |
||
2016 | if ($sex) { |
||
2017 | $sql .= ", i_sex"; |
||
2018 | } |
||
2019 | } |
||
2020 | $rows = $this->runSql($sql); |
||
2021 | if ($simple) { |
||
2022 | if (isset($params[0]) && $params[0] != '') { |
||
2023 | $size = strtolower($params[0]); |
||
2024 | } else { |
||
2025 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
2026 | } |
||
2027 | if (isset($params[1]) && $params[1] != '') { |
||
2028 | $color_from = strtolower($params[1]); |
||
2029 | } else { |
||
2030 | $color_from = $WT_STATS_CHART_COLOR1; |
||
2031 | } |
||
2032 | if (isset($params[2]) && $params[2] != '') { |
||
2033 | $color_to = strtolower($params[2]); |
||
2034 | } else { |
||
2035 | $color_to = $WT_STATS_CHART_COLOR2; |
||
2036 | } |
||
2037 | $sizes = explode('x', $size); |
||
2038 | $tot = 0; |
||
2039 | foreach ($rows as $values) { |
||
2040 | $tot += $values['total']; |
||
2041 | } |
||
2042 | // Beware divide by zero |
||
2043 | if ($tot == 0) { |
||
2044 | return ''; |
||
2045 | } |
||
2046 | $centuries = ""; |
||
2047 | $counts = array(); |
||
2048 | foreach ($rows as $values) { |
||
2049 | $counts[] = round(100 * $values['total'] / $tot, 0); |
||
2050 | $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|'; |
||
2051 | } |
||
2052 | $chd = $this->arrayToExtendedEncoding($counts); |
||
2053 | $chl = rawurlencode(substr($centuries, 0, -1)); |
||
2054 | |||
2055 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Births by century') . "\" title=\"" . I18N::translate('Births by century') . "\" />"; |
||
2056 | } else { |
||
2057 | return $rows; |
||
2058 | } |
||
2059 | } |
||
2060 | |||
2061 | /** |
||
2062 | * Create a chart of death places. |
||
2063 | * |
||
2064 | * @param bool $simple |
||
2065 | * @param bool $sex |
||
2066 | * @param int $year1 |
||
2067 | * @param int $year2 |
||
2068 | * @param string[] $params |
||
2069 | * |
||
2070 | * @return array|string |
||
2071 | */ |
||
2072 | public function statsDeathQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) |
||
2073 | { |
||
2074 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
2075 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
2076 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
2077 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
2078 | |||
2079 | if ($simple) { |
||
2080 | $sql = |
||
2081 | "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total FROM `##dates` " . |
||
2082 | "WHERE " . |
||
2083 | "d_file={$this->tree->getTreeId()} AND " . |
||
2084 | 'd_year<>0 AND ' . |
||
2085 | "d_fact='DEAT' AND " . |
||
2086 | "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
2087 | } elseif ($sex) { |
||
2088 | $sql = |
||
2089 | "SELECT d_month, i_sex, COUNT(*) AS total FROM `##dates` " . |
||
2090 | "JOIN `##individuals` ON d_file = i_file AND d_gid = i_id " . |
||
2091 | "WHERE " . |
||
2092 | "d_file={$this->tree->getTreeId()} AND " . |
||
2093 | "d_fact='DEAT' AND " . |
||
2094 | "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
2095 | } else { |
||
2096 | $sql = |
||
2097 | "SELECT d_month, COUNT(*) AS total FROM `##dates` " . |
||
2098 | "WHERE " . |
||
2099 | "d_file={$this->tree->getTreeId()} AND " . |
||
2100 | "d_fact='DEAT' AND " . |
||
2101 | "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
2102 | } |
||
2103 | if ($year1 >= 0 && $year2 >= 0) { |
||
2104 | $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
2105 | } |
||
2106 | if ($simple) { |
||
2107 | $sql .= " GROUP BY century ORDER BY century"; |
||
2108 | } else { |
||
2109 | $sql .= " GROUP BY d_month"; |
||
2110 | if ($sex) { |
||
2111 | $sql .= ", i_sex"; |
||
2112 | } |
||
2113 | } |
||
2114 | $rows = $this->runSql($sql); |
||
2115 | if ($simple) { |
||
2116 | if (isset($params[0]) && $params[0] != '') { |
||
2117 | $size = strtolower($params[0]); |
||
2118 | } else { |
||
2119 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
2120 | } |
||
2121 | if (isset($params[1]) && $params[1] != '') { |
||
2122 | $color_from = strtolower($params[1]); |
||
2123 | } else { |
||
2124 | $color_from = $WT_STATS_CHART_COLOR1; |
||
2125 | } |
||
2126 | if (isset($params[2]) && $params[2] != '') { |
||
2127 | $color_to = strtolower($params[2]); |
||
2128 | } else { |
||
2129 | $color_to = $WT_STATS_CHART_COLOR2; |
||
2130 | } |
||
2131 | $sizes = explode('x', $size); |
||
2132 | $tot = 0; |
||
2133 | foreach ($rows as $values) { |
||
2134 | $tot += $values['total']; |
||
2135 | } |
||
2136 | // Beware divide by zero |
||
2137 | if ($tot == 0) { |
||
2138 | return ''; |
||
2139 | } |
||
2140 | $centuries = ""; |
||
2141 | $counts = array(); |
||
2142 | foreach ($rows as $values) { |
||
2143 | $counts[] = round(100 * $values['total'] / $tot, 0); |
||
2144 | $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|'; |
||
2145 | } |
||
2146 | $chd = $this->arrayToExtendedEncoding($counts); |
||
2147 | $chl = rawurlencode(substr($centuries, 0, -1)); |
||
2148 | |||
2149 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Deaths by century') . "\" title=\"" . I18N::translate('Deaths by century') . "\" />"; |
||
2150 | } |
||
2151 | |||
2152 | return $rows; |
||
2153 | } |
||
2154 | |||
2155 | /** |
||
2156 | * Find the earliest birth. |
||
2157 | * |
||
2158 | * @return string |
||
2159 | */ |
||
2160 | public function firstBirth() |
||
2161 | { |
||
2162 | return $this->mortalityQuery('full', 'ASC', 'BIRT'); |
||
2163 | } |
||
2164 | |||
2165 | /** |
||
2166 | * Find the earliest birth year. |
||
2167 | * |
||
2168 | * @return string |
||
2169 | */ |
||
2170 | public function firstBirthYear() |
||
2171 | { |
||
2172 | return $this->mortalityQuery('year', 'ASC', 'BIRT'); |
||
2173 | } |
||
2174 | |||
2175 | /** |
||
2176 | * Find the name of the earliest birth. |
||
2177 | * |
||
2178 | * @return string |
||
2179 | */ |
||
2180 | public function firstBirthName() |
||
2181 | { |
||
2182 | return $this->mortalityQuery('name', 'ASC', 'BIRT'); |
||
2183 | } |
||
2184 | |||
2185 | /** |
||
2186 | * Find the earliest birth place. |
||
2187 | * |
||
2188 | * @return string |
||
2189 | */ |
||
2190 | public function firstBirthPlace() |
||
2191 | { |
||
2192 | return $this->mortalityQuery('place', 'ASC', 'BIRT'); |
||
2193 | } |
||
2194 | |||
2195 | /** |
||
2196 | * Find the latest birth. |
||
2197 | * |
||
2198 | * @return string |
||
2199 | */ |
||
2200 | public function lastBirth() |
||
2201 | { |
||
2202 | return $this->mortalityQuery('full', 'DESC', 'BIRT'); |
||
2203 | } |
||
2204 | |||
2205 | /** |
||
2206 | * Find the latest birth year. |
||
2207 | * |
||
2208 | * @return string |
||
2209 | */ |
||
2210 | public function lastBirthYear() |
||
2211 | { |
||
2212 | return $this->mortalityQuery('year', 'DESC', 'BIRT'); |
||
2213 | } |
||
2214 | |||
2215 | /** |
||
2216 | * Find the latest birth name. |
||
2217 | * |
||
2218 | * @return string |
||
2219 | */ |
||
2220 | public function lastBirthName() |
||
2221 | { |
||
2222 | return $this->mortalityQuery('name', 'DESC', 'BIRT'); |
||
2223 | } |
||
2224 | |||
2225 | /** |
||
2226 | * Find the latest birth place. |
||
2227 | * |
||
2228 | * @return string |
||
2229 | */ |
||
2230 | public function lastBirthPlace() |
||
2231 | { |
||
2232 | return $this->mortalityQuery('place', 'DESC', 'BIRT'); |
||
2233 | } |
||
2234 | |||
2235 | /** |
||
2236 | * General query on births. |
||
2237 | * |
||
2238 | * @param string[] $params |
||
2239 | * |
||
2240 | * @return string |
||
2241 | */ |
||
2242 | public function statsBirth($params = array()) |
||
2243 | { |
||
2244 | return $this->statsBirthQuery(true, false, -1, -1, $params); |
||
2245 | } |
||
2246 | |||
2247 | /** |
||
2248 | * Find the earliest death. |
||
2249 | * |
||
2250 | * @return string |
||
2251 | */ |
||
2252 | public function firstDeath() |
||
2253 | { |
||
2254 | return $this->mortalityQuery('full', 'ASC', 'DEAT'); |
||
2255 | } |
||
2256 | |||
2257 | /** |
||
2258 | * Find the earliest death year. |
||
2259 | * |
||
2260 | * @return string |
||
2261 | */ |
||
2262 | public function firstDeathYear() |
||
2263 | { |
||
2264 | return $this->mortalityQuery('year', 'ASC', 'DEAT'); |
||
2265 | } |
||
2266 | |||
2267 | /** |
||
2268 | * Find the earliest death name. |
||
2269 | * |
||
2270 | * @return string |
||
2271 | */ |
||
2272 | public function firstDeathName() |
||
2273 | { |
||
2274 | return $this->mortalityQuery('name', 'ASC', 'DEAT'); |
||
2275 | } |
||
2276 | |||
2277 | /** |
||
2278 | * Find the earliest death place. |
||
2279 | * |
||
2280 | * @return string |
||
2281 | */ |
||
2282 | public function firstDeathPlace() |
||
2283 | { |
||
2284 | return $this->mortalityQuery('place', 'ASC', 'DEAT'); |
||
2285 | } |
||
2286 | |||
2287 | /** |
||
2288 | * Find the latest death. |
||
2289 | * |
||
2290 | * @return string |
||
2291 | */ |
||
2292 | public function lastDeath() |
||
2293 | { |
||
2294 | return $this->mortalityQuery('full', 'DESC', 'DEAT'); |
||
2295 | } |
||
2296 | |||
2297 | /** |
||
2298 | * Find the latest death year. |
||
2299 | * |
||
2300 | * @return string |
||
2301 | */ |
||
2302 | public function lastDeathYear() |
||
2303 | { |
||
2304 | return $this->mortalityQuery('year', 'DESC', 'DEAT'); |
||
2305 | } |
||
2306 | |||
2307 | /** |
||
2308 | * Find the latest death name. |
||
2309 | * |
||
2310 | * @return string |
||
2311 | */ |
||
2312 | public function lastDeathName() |
||
2313 | { |
||
2314 | return $this->mortalityQuery('name', 'DESC', 'DEAT'); |
||
2315 | } |
||
2316 | |||
2317 | /** |
||
2318 | * Find the place of the latest death. |
||
2319 | * |
||
2320 | * @return string |
||
2321 | */ |
||
2322 | public function lastDeathPlace() |
||
2323 | { |
||
2324 | return $this->mortalityQuery('place', 'DESC', 'DEAT'); |
||
2325 | } |
||
2326 | |||
2327 | /** |
||
2328 | * General query on deaths. |
||
2329 | * |
||
2330 | * @param string[] $params |
||
2331 | * |
||
2332 | * @return string |
||
2333 | */ |
||
2334 | public function statsDeath($params = array()) |
||
2335 | { |
||
2336 | return $this->statsDeathQuery(true, false, -1, -1, $params); |
||
2337 | } |
||
2338 | |||
2339 | /** |
||
2340 | * Lifespan |
||
2341 | * |
||
2342 | * @param string $type |
||
2343 | * @param string $sex |
||
2344 | * |
||
2345 | * @return string |
||
2346 | */ |
||
2347 | private function longlifeQuery($type = 'full', $sex = 'F') |
||
2348 | { |
||
2349 | $sex_search = ' 1=1'; |
||
2350 | if ($sex == 'F') { |
||
2351 | $sex_search = " i_sex='F'"; |
||
2352 | } elseif ($sex == 'M') { |
||
2353 | $sex_search = " i_sex='M'"; |
||
2354 | } |
||
2355 | |||
2356 | $rows = $this->runSql( |
||
2357 | " SELECT" . |
||
2358 | " death.d_gid AS id," . |
||
2359 | " death.d_julianday2-birth.d_julianday1 AS age" . |
||
2360 | " FROM" . |
||
2361 | " `##dates` AS death," . |
||
2362 | " `##dates` AS birth," . |
||
2363 | " `##individuals` AS indi" . |
||
2364 | " WHERE" . |
||
2365 | " indi.i_id=birth.d_gid AND" . |
||
2366 | " birth.d_gid=death.d_gid AND" . |
||
2367 | " death.d_file={$this->tree->getTreeId()} AND" . |
||
2368 | " birth.d_file=death.d_file AND" . |
||
2369 | " birth.d_file=indi.i_file AND" . |
||
2370 | " birth.d_fact='BIRT' AND" . |
||
2371 | " death.d_fact='DEAT' AND" . |
||
2372 | " birth.d_julianday1<>0 AND" . |
||
2373 | " death.d_julianday1>birth.d_julianday2 AND" . |
||
2374 | $sex_search . |
||
2375 | " ORDER BY" . |
||
2376 | " age DESC LIMIT 1" |
||
2377 | ); |
||
2378 | if (!isset($rows[0])) { |
||
2379 | return ''; |
||
2380 | } |
||
2381 | $row = $rows[0]; |
||
2382 | $person = Individual::getInstance($row['id'], $this->tree); |
||
2383 | switch ($type) { |
||
2384 | default: |
||
2385 | case 'full': |
||
2386 | if ($person->canShowName()) { |
||
2387 | $result = $person->formatList('span', false, $person->getFullName()); |
||
2388 | } else { |
||
2389 | $result = I18N::translate('This information is private and cannot be shown.'); |
||
2390 | } |
||
2391 | break; |
||
2392 | case 'age': |
||
2393 | $result = I18N::number((int) ($row['age'] / 365.25)); |
||
2394 | break; |
||
2395 | case 'name': |
||
2396 | $result = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a>"; |
||
2397 | break; |
||
2398 | } |
||
2399 | |||
2400 | return $result; |
||
2401 | } |
||
2402 | |||
2403 | /** |
||
2404 | * Find the oldest individuals. |
||
2405 | * |
||
2406 | * @param string $type |
||
2407 | * @param string $sex |
||
2408 | * @param string[] $params |
||
2409 | * |
||
2410 | * @return string |
||
2411 | */ |
||
2412 | private function topTenOldestQuery($type = 'list', $sex = 'BOTH', $params = array()) |
||
2413 | { |
||
2414 | if ($sex === 'F') { |
||
2415 | $sex_search = " AND i_sex='F' "; |
||
2416 | } elseif ($sex === 'M') { |
||
2417 | $sex_search = " AND i_sex='M' "; |
||
2418 | } else { |
||
2419 | $sex_search = ''; |
||
2420 | } |
||
2421 | if (isset($params[0])) { |
||
2422 | $total = (int) $params[0]; |
||
2423 | } else { |
||
2424 | $total = 10; |
||
2425 | } |
||
2426 | $rows = $this->runSql( |
||
2427 | "SELECT " . |
||
2428 | " MAX(death.d_julianday2-birth.d_julianday1) AS age, " . |
||
2429 | " death.d_gid AS deathdate " . |
||
2430 | "FROM " . |
||
2431 | " `##dates` AS death, " . |
||
2432 | " `##dates` AS birth, " . |
||
2433 | " `##individuals` AS indi " . |
||
2434 | "WHERE " . |
||
2435 | " indi.i_id=birth.d_gid AND " . |
||
2436 | " birth.d_gid=death.d_gid AND " . |
||
2437 | " death.d_file={$this->tree->getTreeId()} AND " . |
||
2438 | " birth.d_file=death.d_file AND " . |
||
2439 | " birth.d_file=indi.i_file AND " . |
||
2440 | " birth.d_fact='BIRT' AND " . |
||
2441 | " death.d_fact='DEAT' AND " . |
||
2442 | " birth.d_julianday1<>0 AND " . |
||
2443 | " death.d_julianday1>birth.d_julianday2 " . |
||
2444 | $sex_search . |
||
2445 | "GROUP BY deathdate " . |
||
2446 | "ORDER BY age DESC " . |
||
2447 | "LIMIT " . $total |
||
2448 | ); |
||
2449 | if (!isset($rows[0])) { |
||
2450 | return ''; |
||
2451 | } |
||
2452 | $top10 = array(); |
||
2453 | foreach ($rows as $row) { |
||
2454 | $person = Individual::getInstance($row['deathdate'], $this->tree); |
||
2455 | $age = $row['age']; |
||
2456 | if ((int) ($age / 365.25) > 0) { |
||
2457 | $age = (int) ($age / 365.25) . 'y'; |
||
2458 | } elseif ((int) ($age / 30.4375) > 0) { |
||
2459 | $age = (int) ($age / 30.4375) . 'm'; |
||
2460 | } else { |
||
2461 | $age = $age . 'd'; |
||
2462 | } |
||
2463 | $age = FunctionsDate::getAgeAtEvent($age); |
||
2464 | if ($person->canShow()) { |
||
2465 | if ($type == 'list') { |
||
2466 | $top10[] = "<li><a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")" . "</li>"; |
||
2467 | } else { |
||
2468 | $top10[] = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")"; |
||
2469 | } |
||
2470 | } |
||
2471 | } |
||
2472 | if ($type == 'list') { |
||
2473 | $top10 = implode('', $top10); |
||
2474 | } else { |
||
2475 | $top10 = implode(' ', $top10); |
||
2476 | } |
||
2477 | if (I18N::direction() === 'rtl') { |
||
2478 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
2479 | } |
||
2480 | if ($type == 'list') { |
||
2481 | return '<ul>' . $top10 . '</ul>'; |
||
2482 | } |
||
2483 | |||
2484 | return $top10; |
||
2485 | } |
||
2486 | |||
2487 | /** |
||
2488 | * Find the oldest living individuals. |
||
2489 | * |
||
2490 | * @param string $type |
||
2491 | * @param string $sex |
||
2492 | * @param string[] $params |
||
2493 | * |
||
2494 | * @return string |
||
2495 | */ |
||
2496 | private function topTenOldestAliveQuery($type = 'list', $sex = 'BOTH', $params = array()) |
||
2497 | { |
||
2498 | if (!Auth::isMember($this->tree)) { |
||
2499 | return I18N::translate('This information is private and cannot be shown.'); |
||
2500 | } |
||
2501 | if ($sex == 'F') { |
||
2502 | $sex_search = " AND i_sex='F'"; |
||
2503 | } elseif ($sex == 'M') { |
||
2504 | $sex_search = " AND i_sex='M'"; |
||
2505 | } else { |
||
2506 | $sex_search = ''; |
||
2507 | } |
||
2508 | if (isset($params[0])) { |
||
2509 | $total = (int) $params[0]; |
||
2510 | } else { |
||
2511 | $total = 10; |
||
2512 | } |
||
2513 | $rows = $this->runSql( |
||
2514 | "SELECT" . |
||
2515 | " birth.d_gid AS id," . |
||
2516 | " MIN(birth.d_julianday1) AS age" . |
||
2517 | " FROM" . |
||
2518 | " `##dates` AS birth," . |
||
2519 | " `##individuals` AS indi" . |
||
2520 | " WHERE" . |
||
2521 | " indi.i_id=birth.d_gid AND" . |
||
2522 | " indi.i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")' AND" . |
||
2523 | " birth.d_file={$this->tree->getTreeId()} AND" . |
||
2524 | " birth.d_fact='BIRT' AND" . |
||
2525 | " birth.d_file=indi.i_file AND" . |
||
2526 | " birth.d_julianday1<>0" . |
||
2527 | $sex_search . |
||
2528 | " GROUP BY id" . |
||
2529 | " ORDER BY age" . |
||
2530 | " ASC LIMIT " . $total |
||
2531 | ); |
||
2532 | $top10 = array(); |
||
2533 | foreach ($rows as $row) { |
||
2534 | $person = Individual::getInstance($row['id'], $this->tree); |
||
2535 | $age = (WT_CLIENT_JD - $row['age']); |
||
2536 | if ((int) ($age / 365.25) > 0) { |
||
2537 | $age = (int) ($age / 365.25) . 'y'; |
||
2538 | } elseif ((int) ($age / 30.4375) > 0) { |
||
2539 | $age = (int) ($age / 30.4375) . 'm'; |
||
2540 | } else { |
||
2541 | $age = $age . 'd'; |
||
2542 | } |
||
2543 | $age = FunctionsDate::getAgeAtEvent($age); |
||
2544 | if ($type === 'list') { |
||
2545 | $top10[] = "<li><a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")" . "</li>"; |
||
2546 | } else { |
||
2547 | $top10[] = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")"; |
||
2548 | } |
||
2549 | } |
||
2550 | if ($type === 'list') { |
||
2551 | $top10 = implode('', $top10); |
||
2552 | } else { |
||
2553 | $top10 = implode('; ', $top10); |
||
2554 | } |
||
2555 | if (I18N::direction() === 'rtl') { |
||
2556 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
2557 | } |
||
2558 | if ($type === 'list') { |
||
2559 | return '<ul>' . $top10 . '</ul>'; |
||
2560 | } |
||
2561 | |||
2562 | return $top10; |
||
2563 | } |
||
2564 | |||
2565 | /** |
||
2566 | * Find the average lifespan. |
||
2567 | * |
||
2568 | * @param string $sex |
||
2569 | * @param bool $show_years |
||
2570 | * |
||
2571 | * @return string |
||
2572 | */ |
||
2573 | private function averageLifespanQuery($sex = 'BOTH', $show_years = false) |
||
2574 | { |
||
2575 | if ($sex === 'F') { |
||
2576 | $sex_search = " AND i_sex='F' "; |
||
2577 | } elseif ($sex === 'M') { |
||
2578 | $sex_search = " AND i_sex='M' "; |
||
2579 | } else { |
||
2580 | $sex_search = ''; |
||
2581 | } |
||
2582 | $rows = $this->runSql( |
||
2583 | "SELECT " . |
||
2584 | " AVG(death.d_julianday2-birth.d_julianday1) AS age " . |
||
2585 | "FROM " . |
||
2586 | " `##dates` AS death, " . |
||
2587 | " `##dates` AS birth, " . |
||
2588 | " `##individuals` AS indi " . |
||
2589 | "WHERE " . |
||
2590 | " indi.i_id=birth.d_gid AND " . |
||
2591 | " birth.d_gid=death.d_gid AND " . |
||
2592 | " death.d_file=" . $this->tree->getTreeId() . " AND " . |
||
2593 | " birth.d_file=death.d_file AND " . |
||
2594 | " birth.d_file=indi.i_file AND " . |
||
2595 | " birth.d_fact='BIRT' AND " . |
||
2596 | " death.d_fact='DEAT' AND " . |
||
2597 | " birth.d_julianday1<>0 AND " . |
||
2598 | " death.d_julianday1>birth.d_julianday2 " . |
||
2599 | $sex_search |
||
2600 | ); |
||
2601 | if (!isset($rows[0])) { |
||
2602 | return ''; |
||
2603 | } |
||
2604 | $row = $rows[0]; |
||
2605 | $age = $row['age']; |
||
2606 | if ($show_years) { |
||
2607 | if ((int) ($age / 365.25) > 0) { |
||
2608 | $age = (int) ($age / 365.25) . 'y'; |
||
2609 | } elseif ((int) ($age / 30.4375) > 0) { |
||
2610 | $age = (int) ($age / 30.4375) . 'm'; |
||
2611 | } elseif (!empty($age)) { |
||
2612 | $age = $age . 'd'; |
||
2613 | } |
||
2614 | |||
2615 | return FunctionsDate::getAgeAtEvent($age); |
||
2616 | } else { |
||
2617 | return I18N::number($age / 365.25); |
||
2618 | } |
||
2619 | } |
||
2620 | |||
2621 | /** |
||
2622 | * General query on ages. |
||
2623 | * |
||
2624 | * @param bool $simple |
||
2625 | * @param string $related |
||
2626 | * @param string $sex |
||
2627 | * @param int $year1 |
||
2628 | * @param int $year2 |
||
2629 | * @param string[] $params |
||
2630 | * |
||
2631 | * @return array|string |
||
2632 | */ |
||
2633 | public function statsAgeQuery($simple = true, $related = 'BIRT', $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) |
||
2634 | { |
||
2635 | if ($simple) { |
||
2636 | if (isset($params[0]) && $params[0] != '') { |
||
2637 | $size = strtolower($params[0]); |
||
2638 | } else { |
||
2639 | $size = '230x250'; |
||
2640 | } |
||
2641 | $sizes = explode('x', $size); |
||
2642 | $rows = $this->runSql( |
||
2643 | "SELECT" . |
||
2644 | " ROUND(AVG(death.d_julianday2-birth.d_julianday1)/365.25,1) AS age," . |
||
2645 | " FLOOR(death.d_year/100+1) AS century," . |
||
2646 | " i_sex AS sex" . |
||
2647 | " FROM" . |
||
2648 | " `##dates` AS death," . |
||
2649 | " `##dates` AS birth," . |
||
2650 | " `##individuals` AS indi" . |
||
2651 | " WHERE" . |
||
2652 | " indi.i_id=birth.d_gid AND" . |
||
2653 | " birth.d_gid=death.d_gid AND" . |
||
2654 | " death.d_file={$this->tree->getTreeId()} AND" . |
||
2655 | " birth.d_file=death.d_file AND" . |
||
2656 | " birth.d_file=indi.i_file AND" . |
||
2657 | " birth.d_fact='BIRT' AND" . |
||
2658 | " death.d_fact='DEAT' AND" . |
||
2659 | " birth.d_julianday1<>0 AND" . |
||
2660 | " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" . |
||
2661 | " death.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" . |
||
2662 | " death.d_julianday1>birth.d_julianday2" . |
||
2663 | " GROUP BY century, sex ORDER BY century, sex"); |
||
2664 | if (empty($rows)) { |
||
2665 | return ''; |
||
2666 | } |
||
2667 | $chxl = '0:|'; |
||
2668 | $countsm = ''; |
||
2669 | $countsf = ''; |
||
2670 | $countsa = ''; |
||
2671 | $out = array(); |
||
2672 | foreach ($rows as $values) { |
||
2673 | $out[$values['century']][$values['sex']] = $values['age']; |
||
2674 | } |
||
2675 | foreach ($out as $century => $values) { |
||
2676 | if ($sizes[0] < 980) { |
||
2677 | $sizes[0] += 50; |
||
2678 | } |
||
2679 | $chxl .= $this->centuryName($century) . '|'; |
||
2680 | $average = 0; |
||
2681 | if (isset($values['F'])) { |
||
2682 | $countsf .= $values['F'] . ','; |
||
2683 | $average = $values['F']; |
||
2684 | } else { |
||
2685 | $countsf .= '0,'; |
||
2686 | } |
||
2687 | if (isset($values['M'])) { |
||
2688 | $countsm .= $values['M'] . ','; |
||
2689 | if ($average == 0) { |
||
2690 | $countsa .= $values['M'] . ','; |
||
2691 | } else { |
||
2692 | $countsa .= (($values['M'] + $average) / 2) . ','; |
||
2693 | } |
||
2694 | } else { |
||
2695 | $countsm .= '0,'; |
||
2696 | if ($average == 0) { |
||
2697 | $countsa .= '0,'; |
||
2698 | } else { |
||
2699 | $countsa .= $values['F'] . ','; |
||
2700 | } |
||
2701 | } |
||
2702 | } |
||
2703 | $countsm = substr($countsm, 0, -1); |
||
2704 | $countsf = substr($countsf, 0, -1); |
||
2705 | $countsa = substr($countsa, 0, -1); |
||
2706 | $chd = 't2:' . $countsm . '|' . $countsf . '|' . $countsa; |
||
2707 | $decades = ''; |
||
2708 | for ($i = 0; $i <= 100; $i += 10) { |
||
2709 | $decades .= '|' . I18N::number($i); |
||
2710 | } |
||
2711 | $chxl .= '1:||' . I18N::translate('century') . '|2:' . $decades . '|3:||' . I18N::translate('Age') . '|'; |
||
2712 | $title = I18N::translate('Average age related to death century'); |
||
2713 | if (count($rows) > 6 || mb_strlen($title) < 30) { |
||
2714 | $chtt = $title; |
||
2715 | } else { |
||
2716 | $offset = 0; |
||
2717 | $counter = array(); |
||
2718 | while ($offset = strpos($title, ' ', $offset + 1)) { |
||
2719 | $counter[] = $offset; |
||
2720 | } |
||
2721 | $half = (int) (count($counter) / 2); |
||
2722 | $chtt = substr_replace($title, '|', $counter[$half], 1); |
||
2723 | } |
||
2724 | |||
2725 | return '<img src="' . "https://chart.googleapis.com/chart?cht=bvg&chs={$sizes[0]}x{$sizes[1]}&chm=D,FF0000,2,0,3,1|N*f1*,000000,0,-1,11,1|N*f1*,000000,1,-1,11,1&chf=bg,s,ffffff00|c,s,ffffff00&chtt=" . rawurlencode($chtt) . "&chd={$chd}&chco=0000FF,FFA0CB,FF0000&chbh=20,3&chxt=x,x,y,y&chxl=" . rawurlencode($chxl) . "&chdl=" . rawurlencode(I18N::translate('Males') . '|' . I18N::translate('Females') . '|' . I18N::translate('Average age at death')) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average age related to death century') . "\" title=\"" . I18N::translate('Average age related to death century') . "\" />"; |
||
2726 | } else { |
||
2727 | $sex_search = ''; |
||
2728 | $years = ''; |
||
2729 | if ($sex == 'F') { |
||
2730 | $sex_search = " AND i_sex='F'"; |
||
2731 | } elseif ($sex == 'M') { |
||
2732 | $sex_search = " AND i_sex='M'"; |
||
2733 | } |
||
2734 | if ($year1 >= 0 && $year2 >= 0) { |
||
2735 | if ($related == 'BIRT') { |
||
2736 | $years = " AND birth.d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
2737 | } elseif ($related == 'DEAT') { |
||
2738 | $years = " AND death.d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
2739 | } |
||
2740 | } |
||
2741 | $rows = $this->runSql( |
||
2742 | "SELECT" . |
||
2743 | " death.d_julianday2-birth.d_julianday1 AS age" . |
||
2744 | " FROM" . |
||
2745 | " `##dates` AS death," . |
||
2746 | " `##dates` AS birth," . |
||
2747 | " `##individuals` AS indi" . |
||
2748 | " WHERE" . |
||
2749 | " indi.i_id=birth.d_gid AND" . |
||
2750 | " birth.d_gid=death.d_gid AND" . |
||
2751 | " death.d_file={$this->tree->getTreeId()} AND" . |
||
2752 | " birth.d_file=death.d_file AND" . |
||
2753 | " birth.d_file=indi.i_file AND" . |
||
2754 | " birth.d_fact='BIRT' AND" . |
||
2755 | " death.d_fact='DEAT' AND" . |
||
2756 | " birth.d_julianday1<>0 AND" . |
||
2757 | " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" . |
||
2758 | " death.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" . |
||
2759 | " death.d_julianday1>birth.d_julianday2" . |
||
2760 | $years . |
||
2761 | $sex_search . |
||
2762 | " ORDER BY age DESC"); |
||
2763 | |||
2764 | return $rows; |
||
2765 | } |
||
2766 | } |
||
2767 | |||
2768 | /** |
||
2769 | * General query on ages. |
||
2770 | * |
||
2771 | * @param string[] $params |
||
2772 | * |
||
2773 | * @return string |
||
2774 | */ |
||
2775 | public function statsAge($params = array()) |
||
2776 | { |
||
2777 | return $this->statsAgeQuery(true, 'BIRT', 'BOTH', -1, -1, $params); |
||
2778 | } |
||
2779 | |||
2780 | /** |
||
2781 | * Find the lognest lived individual. |
||
2782 | * |
||
2783 | * @return string |
||
2784 | */ |
||
2785 | public function longestLife() |
||
2786 | { |
||
2787 | return $this->longlifeQuery('full', 'BOTH'); |
||
2788 | } |
||
2789 | |||
2790 | /** |
||
2791 | * Find the age of the longest lived individual. |
||
2792 | * |
||
2793 | * @return string |
||
2794 | */ |
||
2795 | public function longestLifeAge() |
||
2796 | { |
||
2797 | return $this->longlifeQuery('age', 'BOTH'); |
||
2798 | } |
||
2799 | |||
2800 | /** |
||
2801 | * Find the name of the longest lived individual. |
||
2802 | * |
||
2803 | * @return string |
||
2804 | */ |
||
2805 | public function longestLifeName() |
||
2806 | { |
||
2807 | return $this->longlifeQuery('name', 'BOTH'); |
||
2808 | } |
||
2809 | |||
2810 | /** |
||
2811 | * Find the oldest individuals. |
||
2812 | * |
||
2813 | * @param string[] $params |
||
2814 | * |
||
2815 | * @return string |
||
2816 | */ |
||
2817 | public function topTenOldest($params = array()) |
||
2818 | { |
||
2819 | return $this->topTenOldestQuery('nolist', 'BOTH', $params); |
||
2820 | } |
||
2821 | |||
2822 | /** |
||
2823 | * Find the oldest living individuals. |
||
2824 | * |
||
2825 | * @param string[] $params |
||
2826 | * |
||
2827 | * @return string |
||
2828 | */ |
||
2829 | public function topTenOldestList($params = array()) |
||
2830 | { |
||
2831 | return $this->topTenOldestQuery('list', 'BOTH', $params); |
||
2832 | } |
||
2833 | |||
2834 | /** |
||
2835 | * Find the oldest living individuals. |
||
2836 | * |
||
2837 | * @param string[] $params |
||
2838 | * |
||
2839 | * @return string |
||
2840 | */ |
||
2841 | public function topTenOldestAlive($params = array()) |
||
2842 | { |
||
2843 | return $this->topTenOldestAliveQuery('nolist', 'BOTH', $params); |
||
2844 | } |
||
2845 | |||
2846 | /** |
||
2847 | * Find the oldest living individuals. |
||
2848 | * |
||
2849 | * @param string[] $params |
||
2850 | * |
||
2851 | * @return string |
||
2852 | */ |
||
2853 | public function topTenOldestListAlive($params = array()) |
||
2854 | { |
||
2855 | return $this->topTenOldestAliveQuery('list', 'BOTH', $params); |
||
2856 | } |
||
2857 | |||
2858 | /** |
||
2859 | * Find the average lifespan. |
||
2860 | * |
||
2861 | * @param bool $show_years |
||
2862 | * |
||
2863 | * @return string |
||
2864 | */ |
||
2865 | public function averageLifespan($show_years = false) |
||
2866 | { |
||
2867 | return $this->averageLifespanQuery('BOTH', $show_years); |
||
2868 | } |
||
2869 | |||
2870 | /** |
||
2871 | * Find the longest lived female. |
||
2872 | * |
||
2873 | * @return string |
||
2874 | */ |
||
2875 | public function longestLifeFemale() |
||
2876 | { |
||
2877 | return $this->longlifeQuery('full', 'F'); |
||
2878 | } |
||
2879 | |||
2880 | /** |
||
2881 | * Find the age of the longest lived female. |
||
2882 | * |
||
2883 | * @return string |
||
2884 | */ |
||
2885 | public function longestLifeFemaleAge() |
||
2886 | { |
||
2887 | return $this->longlifeQuery('age', 'F'); |
||
2888 | } |
||
2889 | |||
2890 | /** |
||
2891 | * Find the name of the longest lived female. |
||
2892 | * |
||
2893 | * @return string |
||
2894 | */ |
||
2895 | public function longestLifeFemaleName() |
||
2896 | { |
||
2897 | return $this->longlifeQuery('name', 'F'); |
||
2898 | } |
||
2899 | |||
2900 | /** |
||
2901 | * Find the oldest females. |
||
2902 | * |
||
2903 | * @param string[] $params |
||
2904 | * |
||
2905 | * @return string |
||
2906 | */ |
||
2907 | public function topTenOldestFemale($params = array()) |
||
2908 | { |
||
2909 | return $this->topTenOldestQuery('nolist', 'F', $params); |
||
2910 | } |
||
2911 | |||
2912 | /** |
||
2913 | * Find the oldest living females. |
||
2914 | * |
||
2915 | * @param string[] $params |
||
2916 | * |
||
2917 | * @return string |
||
2918 | */ |
||
2919 | public function topTenOldestFemaleList($params = array()) |
||
2920 | { |
||
2921 | return $this->topTenOldestQuery('list', 'F', $params); |
||
2922 | } |
||
2923 | |||
2924 | /** |
||
2925 | * Find the oldest living females. |
||
2926 | * |
||
2927 | * @param string[] $params |
||
2928 | * |
||
2929 | * @return string |
||
2930 | */ |
||
2931 | public function topTenOldestFemaleAlive($params = array()) |
||
2932 | { |
||
2933 | return $this->topTenOldestAliveQuery('nolist', 'F', $params); |
||
2934 | } |
||
2935 | |||
2936 | /** |
||
2937 | * Find the oldest living females. |
||
2938 | * |
||
2939 | * @param string[] $params |
||
2940 | * |
||
2941 | * @return string |
||
2942 | */ |
||
2943 | public function topTenOldestFemaleListAlive($params = array()) |
||
2944 | { |
||
2945 | return $this->topTenOldestAliveQuery('list', 'F', $params); |
||
2946 | } |
||
2947 | |||
2948 | /** |
||
2949 | * Find the average lifespan of females. |
||
2950 | * |
||
2951 | * @param bool $show_years |
||
2952 | * |
||
2953 | * @return string |
||
2954 | */ |
||
2955 | public function averageLifespanFemale($show_years = false) |
||
2956 | { |
||
2957 | return $this->averageLifespanQuery('F', $show_years); |
||
2958 | } |
||
2959 | |||
2960 | /** |
||
2961 | * Find the longest lived male. |
||
2962 | * |
||
2963 | * @return string |
||
2964 | */ |
||
2965 | public function longestLifeMale() |
||
2966 | { |
||
2967 | return $this->longlifeQuery('full', 'M'); |
||
2968 | } |
||
2969 | |||
2970 | /** |
||
2971 | * Find the age of the longest lived male. |
||
2972 | * |
||
2973 | * @return string |
||
2974 | */ |
||
2975 | public function longestLifeMaleAge() |
||
2976 | { |
||
2977 | return $this->longlifeQuery('age', 'M'); |
||
2978 | } |
||
2979 | |||
2980 | /** |
||
2981 | * Find the name of the longest lived male. |
||
2982 | * |
||
2983 | * @return string |
||
2984 | */ |
||
2985 | public function longestLifeMaleName() |
||
2986 | { |
||
2987 | return $this->longlifeQuery('name', 'M'); |
||
2988 | } |
||
2989 | |||
2990 | /** |
||
2991 | * Find the longest lived males. |
||
2992 | * |
||
2993 | * @param string[] $params |
||
2994 | * |
||
2995 | * @return string |
||
2996 | */ |
||
2997 | public function topTenOldestMale($params = array()) |
||
2998 | { |
||
2999 | return $this->topTenOldestQuery('nolist', 'M', $params); |
||
3000 | } |
||
3001 | |||
3002 | /** |
||
3003 | * Find the longest lived males. |
||
3004 | * |
||
3005 | * @param string[] $params |
||
3006 | * |
||
3007 | * @return string |
||
3008 | */ |
||
3009 | public function topTenOldestMaleList($params = array()) |
||
3010 | { |
||
3011 | return $this->topTenOldestQuery('list', 'M', $params); |
||
3012 | } |
||
3013 | |||
3014 | /** |
||
3015 | * Find the longest lived living males. |
||
3016 | * |
||
3017 | * @param string[] $params |
||
3018 | * |
||
3019 | * @return string |
||
3020 | */ |
||
3021 | public function topTenOldestMaleAlive($params = array()) |
||
3022 | { |
||
3023 | return $this->topTenOldestAliveQuery('nolist', 'M', $params); |
||
3024 | } |
||
3025 | |||
3026 | /** |
||
3027 | * Find the longest lived living males. |
||
3028 | * |
||
3029 | * @param string[] $params |
||
3030 | * |
||
3031 | * @return string |
||
3032 | */ |
||
3033 | public function topTenOldestMaleListAlive($params = array()) |
||
3034 | { |
||
3035 | return $this->topTenOldestAliveQuery('list', 'M', $params); |
||
3036 | } |
||
3037 | |||
3038 | /** |
||
3039 | * Find the average male lifespan. |
||
3040 | * |
||
3041 | * @param bool $show_years |
||
3042 | * |
||
3043 | * @return string |
||
3044 | */ |
||
3045 | public function averageLifespanMale($show_years = false) |
||
3046 | { |
||
3047 | return $this->averageLifespanQuery('M', $show_years); |
||
3048 | } |
||
3049 | |||
3050 | /** |
||
3051 | * Events |
||
3052 | * |
||
3053 | * @param string $type |
||
3054 | * @param string $direction |
||
3055 | * @param string $facts |
||
3056 | * |
||
3057 | * @return string |
||
3058 | */ |
||
3059 | private function eventQuery($type, $direction, $facts) |
||
3060 | { |
||
3061 | $eventTypes = array( |
||
3062 | 'BIRT' => I18N::translate('birth'), |
||
3063 | 'DEAT' => I18N::translate('death'), |
||
3064 | 'MARR' => I18N::translate('marriage'), |
||
3065 | 'ADOP' => I18N::translate('adoption'), |
||
3066 | 'BURI' => I18N::translate('burial'), |
||
3067 | 'CENS' => I18N::translate('census added'), |
||
3068 | ); |
||
3069 | |||
3070 | $fact_query = "IN ('" . str_replace('|', "','", $facts) . "')"; |
||
3071 | |||
3072 | if ($direction != 'ASC') { |
||
3073 | $direction = 'DESC'; |
||
3074 | } |
||
3075 | $rows = $this->runSql('' |
||
3076 | . ' SELECT' |
||
3077 | . ' d_gid AS id,' |
||
3078 | . ' d_year AS year,' |
||
3079 | . ' d_fact AS fact,' |
||
3080 | . ' d_type AS type' |
||
3081 | . ' FROM' |
||
3082 | . " `##dates`" |
||
3083 | . ' WHERE' |
||
3084 | . " d_file={$this->tree->getTreeId()} AND" |
||
3085 | . " d_gid<>'HEAD' AND" |
||
3086 | . " d_fact {$fact_query} AND" |
||
3087 | . ' d_julianday1<>0' |
||
3088 | . ' ORDER BY' |
||
3089 | . " d_julianday1 {$direction}, d_type LIMIT 1" |
||
3090 | ); |
||
3091 | if (!isset($rows[0])) { |
||
3092 | return ''; |
||
3093 | } |
||
3094 | $row = $rows[0]; |
||
3095 | $record = GedcomRecord::getInstance($row['id'], $this->tree); |
||
3096 | switch ($type) { |
||
3097 | default: |
||
3098 | case 'full': |
||
3099 | if ($record->canShow()) { |
||
3100 | $result = $record->formatList('span', false, $record->getFullName()); |
||
3101 | } else { |
||
3102 | $result = I18N::translate('This information is private and cannot be shown.'); |
||
3103 | } |
||
3104 | break; |
||
3105 | case 'year': |
||
3106 | $date = new Date($row['type'] . ' ' . $row['year']); |
||
3107 | $result = $date->display(); |
||
3108 | break; |
||
3109 | case 'type': |
||
3110 | if (isset($eventTypes[$row['fact']])) { |
||
3111 | $result = $eventTypes[$row['fact']]; |
||
3112 | } else { |
||
3113 | $result = GedcomTag::getLabel($row['fact']); |
||
3114 | } |
||
3115 | break; |
||
3116 | case 'name': |
||
3117 | $result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>"; |
||
3118 | break; |
||
3119 | case 'place': |
||
3120 | $fact = $record->getFirstFact($row['fact']); |
||
3121 | if ($fact) { |
||
3122 | $result = FunctionsPrint::formatFactPlace($fact, true, true, true); |
||
3123 | } else { |
||
3124 | $result = I18N::translate('Private'); |
||
3125 | } |
||
3126 | break; |
||
3127 | } |
||
3128 | |||
3129 | return $result; |
||
3130 | } |
||
3131 | |||
3132 | /** |
||
3133 | * Find the earliest event. |
||
3134 | * |
||
3135 | * @return string |
||
3136 | */ |
||
3137 | public function firstEvent() |
||
3138 | { |
||
3139 | return $this->eventQuery('full', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3140 | } |
||
3141 | |||
3142 | /** |
||
3143 | * Find the year of the earliest event. |
||
3144 | * |
||
3145 | * @return string |
||
3146 | */ |
||
3147 | public function firstEventYear() |
||
3148 | { |
||
3149 | return $this->eventQuery('year', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3150 | } |
||
3151 | |||
3152 | /** |
||
3153 | * Find the type of the earliest event. |
||
3154 | * |
||
3155 | * @return string |
||
3156 | */ |
||
3157 | public function firstEventType() |
||
3158 | { |
||
3159 | return $this->eventQuery('type', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3160 | } |
||
3161 | |||
3162 | /** |
||
3163 | * Find the name of the individual with the earliest event. |
||
3164 | * |
||
3165 | * @return string |
||
3166 | */ |
||
3167 | public function firstEventName() |
||
3168 | { |
||
3169 | return $this->eventQuery('name', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3170 | } |
||
3171 | |||
3172 | /** |
||
3173 | * Find the location of the earliest event. |
||
3174 | * |
||
3175 | * @return string |
||
3176 | */ |
||
3177 | public function firstEventPlace() |
||
3178 | { |
||
3179 | return $this->eventQuery('place', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3180 | } |
||
3181 | |||
3182 | /** |
||
3183 | * Find the latest event. |
||
3184 | * |
||
3185 | * @return string |
||
3186 | */ |
||
3187 | public function lastEvent() |
||
3188 | { |
||
3189 | return $this->eventQuery('full', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3190 | } |
||
3191 | |||
3192 | /** |
||
3193 | * Find the year of the latest event. |
||
3194 | * |
||
3195 | * @return string |
||
3196 | */ |
||
3197 | public function lastEventYear() |
||
3198 | { |
||
3199 | return $this->eventQuery('year', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3200 | } |
||
3201 | |||
3202 | /** |
||
3203 | * Find the type of the latest event. |
||
3204 | * |
||
3205 | * @return string |
||
3206 | */ |
||
3207 | public function lastEventType() |
||
3208 | { |
||
3209 | return $this->eventQuery('type', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3210 | } |
||
3211 | |||
3212 | /** |
||
3213 | * Find the name of the individual with the latest event. |
||
3214 | * |
||
3215 | * @return string |
||
3216 | */ |
||
3217 | public function lastEventName() |
||
3218 | { |
||
3219 | return $this->eventQuery('name', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3220 | } |
||
3221 | |||
3222 | /** |
||
3223 | * FInd the location of the latest event. |
||
3224 | * |
||
3225 | * @return string |
||
3226 | */ |
||
3227 | public function lastEventPlace() |
||
3228 | { |
||
3229 | return $this->eventQuery('place', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT); |
||
3230 | } |
||
3231 | |||
3232 | /** |
||
3233 | * Query the database for marriage tags. |
||
3234 | * |
||
3235 | * @param string $type |
||
3236 | * @param string $age_dir |
||
3237 | * @param string $sex |
||
3238 | * @param bool $show_years |
||
3239 | * |
||
3240 | * @return string |
||
3241 | */ |
||
3242 | private function marriageQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) |
||
3243 | { |
||
3244 | if ($sex == 'F') { |
||
3245 | $sex_field = 'f_wife'; |
||
3246 | } else { |
||
3247 | $sex_field = 'f_husb'; |
||
3248 | } |
||
3249 | if ($age_dir != 'ASC') { |
||
3250 | $age_dir = 'DESC'; |
||
3251 | } |
||
3252 | $rows = $this->runSql( |
||
3253 | " SELECT fam.f_id AS famid, fam.{$sex_field}, married.d_julianday2-birth.d_julianday1 AS age, indi.i_id AS i_id" . |
||
3254 | " FROM `##families` AS fam" . |
||
3255 | " LEFT JOIN `##dates` AS birth ON birth.d_file = {$this->tree->getTreeId()}" . |
||
3256 | " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" . |
||
3257 | " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" . |
||
3258 | " WHERE" . |
||
3259 | " birth.d_gid = indi.i_id AND" . |
||
3260 | " married.d_gid = fam.f_id AND" . |
||
3261 | " indi.i_id = fam.{$sex_field} AND" . |
||
3262 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
3263 | " birth.d_fact = 'BIRT' AND" . |
||
3264 | " married.d_fact = 'MARR' AND" . |
||
3265 | " birth.d_julianday1 <> 0 AND" . |
||
3266 | " married.d_julianday2 > birth.d_julianday1 AND" . |
||
3267 | " i_sex='{$sex}'" . |
||
3268 | " ORDER BY" . |
||
3269 | " married.d_julianday2-birth.d_julianday1 {$age_dir} LIMIT 1" |
||
3270 | ); |
||
3271 | if (!isset($rows[0])) { |
||
3272 | return ''; |
||
3273 | } |
||
3274 | $row = $rows[0]; |
||
3275 | if (isset($row['famid'])) { |
||
3276 | $family = Family::getInstance($row['famid'], $this->tree); |
||
3277 | } |
||
3278 | if (isset($row['i_id'])) { |
||
3279 | $person = Individual::getInstance($row['i_id'], $this->tree); |
||
3280 | } |
||
3281 | switch ($type) { |
||
3282 | default: |
||
3283 | case 'full': |
||
3284 | if ($family->canShow()) { |
||
3285 | $result = $family->formatList('span', false, $person->getFullName()); |
||
3286 | } else { |
||
3287 | $result = I18N::translate('This information is private and cannot be shown.'); |
||
3288 | } |
||
3289 | break; |
||
3290 | case 'name': |
||
3291 | $result = '<a href="' . $family->getHtmlUrl() . '">' . $person->getFullName() . '</a>'; |
||
3292 | break; |
||
3293 | case 'age': |
||
3294 | $age = $row['age']; |
||
3295 | if ($show_years) { |
||
3296 | if ((int) ($age / 365.25) > 0) { |
||
3297 | $age = (int) ($age / 365.25) . 'y'; |
||
3298 | } elseif ((int) ($age / 30.4375) > 0) { |
||
3299 | $age = (int) ($age / 30.4375) . 'm'; |
||
3300 | } else { |
||
3301 | $age = $age . 'd'; |
||
3302 | } |
||
3303 | $result = FunctionsDate::getAgeAtEvent($age); |
||
3304 | } else { |
||
3305 | $result = I18N::number((int) ($age / 365.25)); |
||
3306 | } |
||
3307 | break; |
||
3308 | } |
||
3309 | |||
3310 | return $result; |
||
3311 | } |
||
3312 | |||
3313 | /** |
||
3314 | * General query on age at marriage. |
||
3315 | * |
||
3316 | * @param string $type |
||
3317 | * @param string $age_dir |
||
3318 | * @param string[] $params |
||
3319 | * |
||
3320 | * @return string |
||
3321 | */ |
||
3322 | private function ageOfMarriageQuery($type = 'list', $age_dir = 'ASC', $params = array()) |
||
3323 | { |
||
3324 | if (isset($params[0])) { |
||
3325 | $total = (int) $params[0]; |
||
3326 | } else { |
||
3327 | $total = 10; |
||
3328 | } |
||
3329 | if ($age_dir != 'ASC') { |
||
3330 | $age_dir = 'DESC'; |
||
3331 | } |
||
3332 | $hrows = $this->runSql( |
||
3333 | " SELECT DISTINCT fam.f_id AS family, MIN(husbdeath.d_julianday2-married.d_julianday1) AS age" . |
||
3334 | " FROM `##families` AS fam" . |
||
3335 | " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" . |
||
3336 | " LEFT JOIN `##dates` AS husbdeath ON husbdeath.d_file = {$this->tree->getTreeId()}" . |
||
3337 | " WHERE" . |
||
3338 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
3339 | " husbdeath.d_gid = fam.f_husb AND" . |
||
3340 | " husbdeath.d_fact = 'DEAT' AND" . |
||
3341 | " married.d_gid = fam.f_id AND" . |
||
3342 | " married.d_fact = 'MARR' AND" . |
||
3343 | " married.d_julianday1 < husbdeath.d_julianday2 AND" . |
||
3344 | " married.d_julianday1 <> 0" . |
||
3345 | " GROUP BY family" . |
||
3346 | " ORDER BY age {$age_dir}"); |
||
3347 | $wrows = $this->runSql( |
||
3348 | " SELECT DISTINCT fam.f_id AS family, MIN(wifedeath.d_julianday2-married.d_julianday1) AS age" . |
||
3349 | " FROM `##families` AS fam" . |
||
3350 | " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" . |
||
3351 | " LEFT JOIN `##dates` AS wifedeath ON wifedeath.d_file = {$this->tree->getTreeId()}" . |
||
3352 | " WHERE" . |
||
3353 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
3354 | " wifedeath.d_gid = fam.f_wife AND" . |
||
3355 | " wifedeath.d_fact = 'DEAT' AND" . |
||
3356 | " married.d_gid = fam.f_id AND" . |
||
3357 | " married.d_fact = 'MARR' AND" . |
||
3358 | " married.d_julianday1 < wifedeath.d_julianday2 AND" . |
||
3359 | " married.d_julianday1 <> 0" . |
||
3360 | " GROUP BY family" . |
||
3361 | " ORDER BY age {$age_dir}"); |
||
3362 | $drows = $this->runSql( |
||
3363 | " SELECT DISTINCT fam.f_id AS family, MIN(divorced.d_julianday2-married.d_julianday1) AS age" . |
||
3364 | " FROM `##families` AS fam" . |
||
3365 | " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" . |
||
3366 | " LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->getTreeId()}" . |
||
3367 | " WHERE" . |
||
3368 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
3369 | " married.d_gid = fam.f_id AND" . |
||
3370 | " married.d_fact = 'MARR' AND" . |
||
3371 | " divorced.d_gid = fam.f_id AND" . |
||
3372 | " divorced.d_fact IN ('DIV', 'ANUL', '_SEPR', '_DETS') AND" . |
||
3373 | " married.d_julianday1 < divorced.d_julianday2 AND" . |
||
3374 | " married.d_julianday1 <> 0" . |
||
3375 | " GROUP BY family" . |
||
3376 | " ORDER BY age {$age_dir}"); |
||
3377 | if (!isset($hrows) && !isset($wrows) && !isset($drows)) { |
||
3378 | return ''; |
||
3379 | } |
||
3380 | $rows = array(); |
||
3381 | foreach ($drows as $family) { |
||
3382 | $rows[$family['family']] = $family['age']; |
||
3383 | } |
||
3384 | foreach ($hrows as $family) { |
||
3385 | if (!isset($rows[$family['family']])) { |
||
3386 | $rows[$family['family']] = $family['age']; |
||
3387 | } |
||
3388 | } |
||
3389 | foreach ($wrows as $family) { |
||
3390 | if (!isset($rows[$family['family']])) { |
||
3391 | $rows[$family['family']] = $family['age']; |
||
3392 | } elseif ($rows[$family['family']] > $family['age']) { |
||
3393 | $rows[$family['family']] = $family['age']; |
||
3394 | } |
||
3395 | } |
||
3396 | if ($age_dir === 'DESC') { |
||
3397 | arsort($rows); |
||
3398 | } else { |
||
3399 | asort($rows); |
||
3400 | } |
||
3401 | $top10 = array(); |
||
3402 | $i = 0; |
||
3403 | foreach ($rows as $fam => $age) { |
||
3404 | $family = Family::getInstance($fam, $this->tree); |
||
3405 | if ($type === 'name') { |
||
3406 | return $family->formatList('span', false, $family->getFullName()); |
||
3407 | } |
||
3408 | if ((int) ($age / 365.25) > 0) { |
||
3409 | $age = (int) ($age / 365.25) . 'y'; |
||
3410 | } elseif ((int) ($age / 30.4375) > 0) { |
||
3411 | $age = (int) ($age / 30.4375) . 'm'; |
||
3412 | } else { |
||
3413 | $age = $age . 'd'; |
||
3414 | } |
||
3415 | $age = FunctionsDate::getAgeAtEvent($age); |
||
3416 | if ($type === 'age') { |
||
3417 | return $age; |
||
3418 | } |
||
3419 | $husb = $family->getHusband(); |
||
3420 | $wife = $family->getWife(); |
||
3421 | if ($husb && $wife && ($husb->getAllDeathDates() && $wife->getAllDeathDates() || !$husb->isDead() || !$wife->isDead())) { |
||
3422 | if ($family->canShow()) { |
||
3423 | if ($type === 'list') { |
||
3424 | $top10[] = "<li><a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a> (" . $age . ")" . "</li>"; |
||
3425 | } else { |
||
3426 | $top10[] = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a> (" . $age . ")"; |
||
3427 | } |
||
3428 | } |
||
3429 | if (++$i === $total) { |
||
3430 | break; |
||
3431 | } |
||
3432 | } |
||
3433 | } |
||
3434 | if ($type === 'list') { |
||
3435 | $top10 = implode('', $top10); |
||
3436 | } else { |
||
3437 | $top10 = implode('; ', $top10); |
||
3438 | } |
||
3439 | if (I18N::direction() === 'rtl') { |
||
3440 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
3441 | } |
||
3442 | if ($type === 'list') { |
||
3443 | return '<ul>' . $top10 . '</ul>'; |
||
3444 | } |
||
3445 | |||
3446 | return $top10; |
||
3447 | } |
||
3448 | |||
3449 | /** |
||
3450 | * Find the ages between spouses. |
||
3451 | * |
||
3452 | * @param string $type |
||
3453 | * @param string $age_dir |
||
3454 | * @param string[] $params |
||
3455 | * |
||
3456 | * @return string |
||
3457 | */ |
||
3458 | private function ageBetweenSpousesQuery($type = 'list', $age_dir = 'DESC', $params = array()) |
||
3459 | { |
||
3460 | if (isset($params[0])) { |
||
3461 | $total = (int) $params[0]; |
||
3462 | } else { |
||
3463 | $total = 10; |
||
3464 | } |
||
3465 | if ($age_dir === 'DESC') { |
||
3466 | $sql = |
||
3467 | "SELECT f_id AS xref, MIN(wife.d_julianday2-husb.d_julianday1) AS age" . |
||
3468 | " FROM `##families`" . |
||
3469 | " JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" . |
||
3470 | " JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" . |
||
3471 | " WHERE f_file = :tree_id" . |
||
3472 | " AND husb.d_fact = 'BIRT'" . |
||
3473 | " AND wife.d_fact = 'BIRT'" . |
||
3474 | " AND wife.d_julianday2 >= husb.d_julianday1 AND husb.d_julianday1 <> 0" . |
||
3475 | " GROUP BY xref" . |
||
3476 | " ORDER BY age DESC" . |
||
3477 | " LIMIT :limit"; |
||
3478 | } else { |
||
3479 | $sql = |
||
3480 | "SELECT f_id AS xref, MIN(husb.d_julianday2-wife.d_julianday1) AS age" . |
||
3481 | " FROM `##families`" . |
||
3482 | " JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" . |
||
3483 | " JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" . |
||
3484 | " WHERE f_file = :tree_id" . |
||
3485 | " AND husb.d_fact = 'BIRT'" . |
||
3486 | " AND wife.d_fact = 'BIRT'" . |
||
3487 | " AND husb.d_julianday2 >= wife.d_julianday1 AND wife.d_julianday1 <> 0" . |
||
3488 | " GROUP BY xref" . |
||
3489 | " ORDER BY age DESC" . |
||
3490 | " LIMIT :limit"; |
||
3491 | } |
||
3492 | $rows = Database::prepare( |
||
3493 | $sql |
||
3494 | )->execute(array( |
||
3495 | 'tree_id' => $this->tree->getTreeId(), |
||
3496 | 'limit' => $total, |
||
3497 | ))->fetchAll(); |
||
3498 | |||
3499 | $top10 = array(); |
||
3500 | foreach ($rows as $fam) { |
||
3501 | $family = Family::getInstance($fam->xref, $this->tree); |
||
3502 | if ($fam->age < 0) { |
||
3503 | break; |
||
3504 | } |
||
3505 | $age = $fam->age; |
||
3506 | if ((int) ($age / 365.25) > 0) { |
||
3507 | $age = (int) ($age / 365.25) . 'y'; |
||
3508 | } elseif ((int) ($age / 30.4375) > 0) { |
||
3509 | $age = (int) ($age / 30.4375) . 'm'; |
||
3510 | } else { |
||
3511 | $age = $age . 'd'; |
||
3512 | } |
||
3513 | $age = FunctionsDate::getAgeAtEvent($age); |
||
3514 | if ($family->canShow()) { |
||
3515 | if ($type === 'list') { |
||
3516 | $top10[] = '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> (' . $age . ')' . "</li>"; |
||
3517 | } else { |
||
3518 | $top10[] = '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> (' . $age . ')'; |
||
3519 | } |
||
3520 | } |
||
3521 | } |
||
3522 | if ($type === 'list') { |
||
3523 | $top10 = implode('', $top10); |
||
3524 | if ($top10) { |
||
3525 | $top10 = '<ul>' . $top10 . '</ul>'; |
||
3526 | } |
||
3527 | } else { |
||
3528 | $top10 = implode(' ', $top10); |
||
3529 | } |
||
3530 | |||
3531 | return $top10; |
||
3532 | } |
||
3533 | |||
3534 | /** |
||
3535 | * General query on parents. |
||
3536 | * |
||
3537 | * @param string $type |
||
3538 | * @param string $age_dir |
||
3539 | * @param string $sex |
||
3540 | * @param bool $show_years |
||
3541 | * |
||
3542 | * @return string |
||
3543 | */ |
||
3544 | private function parentsQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) |
||
3545 | { |
||
3546 | if ($sex == 'F') { |
||
3547 | $sex_field = 'WIFE'; |
||
3548 | } else { |
||
3549 | $sex_field = 'HUSB'; |
||
3550 | } |
||
3551 | if ($age_dir != 'ASC') { |
||
3552 | $age_dir = 'DESC'; |
||
3553 | } |
||
3554 | $rows = $this->runSql( |
||
3555 | " SELECT" . |
||
3556 | " parentfamily.l_to AS id," . |
||
3557 | " childbirth.d_julianday2-birth.d_julianday1 AS age" . |
||
3558 | " FROM `##link` AS parentfamily" . |
||
3559 | " JOIN `##link` AS childfamily ON childfamily.l_file = {$this->tree->getTreeId()}" . |
||
3560 | " JOIN `##dates` AS birth ON birth.d_file = {$this->tree->getTreeId()}" . |
||
3561 | " JOIN `##dates` AS childbirth ON childbirth.d_file = {$this->tree->getTreeId()}" . |
||
3562 | " WHERE" . |
||
3563 | " birth.d_gid = parentfamily.l_to AND" . |
||
3564 | " childfamily.l_to = childbirth.d_gid AND" . |
||
3565 | " childfamily.l_type = 'CHIL' AND" . |
||
3566 | " parentfamily.l_type = '{$sex_field}' AND" . |
||
3567 | " childfamily.l_from = parentfamily.l_from AND" . |
||
3568 | " parentfamily.l_file = {$this->tree->getTreeId()} AND" . |
||
3569 | " birth.d_fact = 'BIRT' AND" . |
||
3570 | " childbirth.d_fact = 'BIRT' AND" . |
||
3571 | " birth.d_julianday1 <> 0 AND" . |
||
3572 | " childbirth.d_julianday2 > birth.d_julianday1" . |
||
3573 | " ORDER BY age {$age_dir} LIMIT 1" |
||
3574 | ); |
||
3575 | if (!isset($rows[0])) { |
||
3576 | return ''; |
||
3577 | } |
||
3578 | $row = $rows[0]; |
||
3579 | if (isset($row['id'])) { |
||
3580 | $person = Individual::getInstance($row['id'], $this->tree); |
||
3581 | } |
||
3582 | switch ($type) { |
||
3583 | default: |
||
3584 | case 'full': |
||
3585 | if ($person->canShow()) { |
||
3586 | $result = $person->formatList('span', false, $person->getFullName()); |
||
3587 | } else { |
||
3588 | $result = I18N::translate('This information is private and cannot be shown.'); |
||
3589 | } |
||
3590 | break; |
||
3591 | case 'name': |
||
3592 | $result = '<a href="' . $person->getHtmlUrl() . '">' . $person->getFullName() . '</a>'; |
||
3593 | break; |
||
3594 | case 'age': |
||
3595 | $age = $row['age']; |
||
3596 | if ($show_years) { |
||
3597 | if ((int) ($age / 365.25) > 0) { |
||
3598 | $age = (int) ($age / 365.25) . 'y'; |
||
3599 | } elseif ((int) ($age / 30.4375) > 0) { |
||
3600 | $age = (int) ($age / 30.4375) . 'm'; |
||
3601 | } else { |
||
3602 | $age = $age . 'd'; |
||
3603 | } |
||
3604 | $result = FunctionsDate::getAgeAtEvent($age); |
||
3605 | } else { |
||
3606 | $result = (int) ($age / 365.25); |
||
3607 | } |
||
3608 | break; |
||
3609 | } |
||
3610 | |||
3611 | return $result; |
||
3612 | } |
||
3613 | |||
3614 | /** |
||
3615 | * General query on marriages. |
||
3616 | * |
||
3617 | * @param bool $simple |
||
3618 | * @param bool $first |
||
3619 | * @param int $year1 |
||
3620 | * @param int $year2 |
||
3621 | * @param string[] $params |
||
3622 | * |
||
3623 | * @return string|array |
||
3624 | */ |
||
3625 | public function statsMarrQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) |
||
3626 | { |
||
3627 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
3628 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
3629 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
3630 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
3631 | |||
3632 | if ($simple) { |
||
3633 | $sql = |
||
3634 | "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total" . |
||
3635 | " FROM `##dates`" . |
||
3636 | " WHERE d_file={$this->tree->getTreeId()} AND d_year<>0 AND d_fact='MARR' AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
3637 | if ($year1 >= 0 && $year2 >= 0) { |
||
3638 | $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
3639 | } |
||
3640 | $sql .= " GROUP BY century ORDER BY century"; |
||
3641 | } elseif ($first) { |
||
3642 | $years = ''; |
||
3643 | if ($year1 >= 0 && $year2 >= 0) { |
||
3644 | $years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND"; |
||
3645 | } |
||
3646 | $sql = |
||
3647 | " SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, married.d_julianday2 AS age, married.d_month AS month, indi.i_id AS indi" . |
||
3648 | " FROM `##families` AS fam" . |
||
3649 | " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" . |
||
3650 | " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" . |
||
3651 | " WHERE" . |
||
3652 | " married.d_gid = fam.f_id AND" . |
||
3653 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
3654 | " married.d_fact = 'MARR' AND" . |
||
3655 | " married.d_julianday2 <> 0 AND" . |
||
3656 | $years . |
||
3657 | " (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" . |
||
3658 | " ORDER BY fams, indi, age ASC"; |
||
3659 | } else { |
||
3660 | $sql = |
||
3661 | "SELECT d_month, COUNT(*) AS total" . |
||
3662 | " FROM `##dates`" . |
||
3663 | " WHERE d_file={$this->tree->getTreeId()} AND d_fact='MARR'"; |
||
3664 | if ($year1 >= 0 && $year2 >= 0) { |
||
3665 | $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
3666 | } |
||
3667 | $sql .= " GROUP BY d_month"; |
||
3668 | } |
||
3669 | $rows = $this->runSql($sql); |
||
3670 | if (!isset($rows)) { |
||
3671 | return ''; |
||
3672 | } |
||
3673 | if ($simple) { |
||
3674 | if (isset($params[0]) && $params[0] != '') { |
||
3675 | $size = strtolower($params[0]); |
||
3676 | } else { |
||
3677 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
3678 | } |
||
3679 | if (isset($params[1]) && $params[1] != '') { |
||
3680 | $color_from = strtolower($params[1]); |
||
3681 | } else { |
||
3682 | $color_from = $WT_STATS_CHART_COLOR1; |
||
3683 | } |
||
3684 | if (isset($params[2]) && $params[2] != '') { |
||
3685 | $color_to = strtolower($params[2]); |
||
3686 | } else { |
||
3687 | $color_to = $WT_STATS_CHART_COLOR2; |
||
3688 | } |
||
3689 | $sizes = explode('x', $size); |
||
3690 | $tot = 0; |
||
3691 | foreach ($rows as $values) { |
||
3692 | $tot += (int) $values['total']; |
||
3693 | } |
||
3694 | // Beware divide by zero |
||
3695 | if ($tot === 0) { |
||
3696 | return ''; |
||
3697 | } |
||
3698 | $centuries = ''; |
||
3699 | $counts = array(); |
||
3700 | foreach ($rows as $values) { |
||
3701 | $counts[] = round(100 * $values['total'] / $tot, 0); |
||
3702 | $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|'; |
||
3703 | } |
||
3704 | $chd = $this->arrayToExtendedEncoding($counts); |
||
3705 | $chl = substr($centuries, 0, -1); |
||
3706 | |||
3707 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Marriages by century') . "\" title=\"" . I18N::translate('Marriages by century') . "\" />"; |
||
3708 | } |
||
3709 | |||
3710 | return $rows; |
||
3711 | } |
||
3712 | |||
3713 | /** |
||
3714 | * General query on divorces. |
||
3715 | * |
||
3716 | * @param bool $simple |
||
3717 | * @param bool $first |
||
3718 | * @param int $year1 |
||
3719 | * @param int $year2 |
||
3720 | * @param string[] $params |
||
3721 | * |
||
3722 | * @return string|array |
||
3723 | */ |
||
3724 | private function statsDivQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) |
||
3725 | { |
||
3726 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
3727 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
3728 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
3729 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
3730 | |||
3731 | if ($simple) { |
||
3732 | $sql = |
||
3733 | "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total" . |
||
3734 | " FROM `##dates`" . |
||
3735 | " WHERE d_file={$this->tree->getTreeId()} AND d_year<>0 AND d_fact = 'DIV' AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')"; |
||
3736 | if ($year1 >= 0 && $year2 >= 0) { |
||
3737 | $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
3738 | } |
||
3739 | $sql .= " GROUP BY century ORDER BY century"; |
||
3740 | } elseif ($first) { |
||
3741 | $years = ''; |
||
3742 | if ($year1 >= 0 && $year2 >= 0) { |
||
3743 | $years = " divorced.d_year BETWEEN '{$year1}' AND '{$year2}' AND"; |
||
3744 | } |
||
3745 | $sql = |
||
3746 | " SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, divorced.d_julianday2 AS age, divorced.d_month AS month, indi.i_id AS indi" . |
||
3747 | " FROM `##families` AS fam" . |
||
3748 | " LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->getTreeId()}" . |
||
3749 | " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" . |
||
3750 | " WHERE" . |
||
3751 | " divorced.d_gid = fam.f_id AND" . |
||
3752 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
3753 | " divorced.d_fact = 'DIV' AND" . |
||
3754 | " divorced.d_julianday2 <> 0 AND" . |
||
3755 | $years . |
||
3756 | " (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" . |
||
3757 | " ORDER BY fams, indi, age ASC"; |
||
3758 | } else { |
||
3759 | $sql = |
||
3760 | "SELECT d_month, COUNT(*) AS total FROM `##dates` " . |
||
3761 | "WHERE d_file={$this->tree->getTreeId()} AND d_fact = 'DIV'"; |
||
3762 | if ($year1 >= 0 && $year2 >= 0) { |
||
3763 | $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
3764 | } |
||
3765 | $sql .= " GROUP BY d_month"; |
||
3766 | } |
||
3767 | $rows = $this->runSql($sql); |
||
3768 | if (!isset($rows)) { |
||
3769 | return ''; |
||
3770 | } |
||
3771 | if ($simple) { |
||
3772 | if (isset($params[0]) && $params[0] != '') { |
||
3773 | $size = strtolower($params[0]); |
||
3774 | } else { |
||
3775 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
3776 | } |
||
3777 | if (isset($params[1]) && $params[1] != '') { |
||
3778 | $color_from = strtolower($params[1]); |
||
3779 | } else { |
||
3780 | $color_from = $WT_STATS_CHART_COLOR1; |
||
3781 | } |
||
3782 | if (isset($params[2]) && $params[2] != '') { |
||
3783 | $color_to = strtolower($params[2]); |
||
3784 | } else { |
||
3785 | $color_to = $WT_STATS_CHART_COLOR2; |
||
3786 | } |
||
3787 | $sizes = explode('x', $size); |
||
3788 | $tot = 0; |
||
3789 | foreach ($rows as $values) { |
||
3790 | $tot += (int) $values['total']; |
||
3791 | } |
||
3792 | // Beware divide by zero |
||
3793 | if ($tot === 0) { |
||
3794 | return ''; |
||
3795 | } |
||
3796 | $centuries = ''; |
||
3797 | $counts = array(); |
||
3798 | foreach ($rows as $values) { |
||
3799 | $counts[] = round(100 * $values['total'] / $tot, 0); |
||
3800 | $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|'; |
||
3801 | } |
||
3802 | $chd = $this->arrayToExtendedEncoding($counts); |
||
3803 | $chl = substr($centuries, 0, -1); |
||
3804 | |||
3805 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Divorces by century') . "\" title=\"" . I18N::translate('Divorces by century') . "\" />"; |
||
3806 | } |
||
3807 | |||
3808 | return $rows; |
||
3809 | } |
||
3810 | |||
3811 | /** |
||
3812 | * Find the earliest marriage. |
||
3813 | * |
||
3814 | * @return string |
||
3815 | */ |
||
3816 | public function firstMarriage() |
||
3817 | { |
||
3818 | return $this->mortalityQuery('full', 'ASC', 'MARR'); |
||
3819 | } |
||
3820 | |||
3821 | /** |
||
3822 | * Find the year of the earliest marriage. |
||
3823 | * |
||
3824 | * @return string |
||
3825 | */ |
||
3826 | public function firstMarriageYear() |
||
3827 | { |
||
3828 | return $this->mortalityQuery('year', 'ASC', 'MARR'); |
||
3829 | } |
||
3830 | |||
3831 | /** |
||
3832 | * Find the names of spouses of the earliest marriage. |
||
3833 | * |
||
3834 | * @return string |
||
3835 | */ |
||
3836 | public function firstMarriageName() |
||
3837 | { |
||
3838 | return $this->mortalityQuery('name', 'ASC', 'MARR'); |
||
3839 | } |
||
3840 | |||
3841 | /** |
||
3842 | * Find the place of the earliest marriage. |
||
3843 | * |
||
3844 | * @return string |
||
3845 | */ |
||
3846 | public function firstMarriagePlace() |
||
3847 | { |
||
3848 | return $this->mortalityQuery('place', 'ASC', 'MARR'); |
||
3849 | } |
||
3850 | |||
3851 | /** |
||
3852 | * Find the latest marriage. |
||
3853 | * |
||
3854 | * @return string |
||
3855 | */ |
||
3856 | public function lastMarriage() |
||
3857 | { |
||
3858 | return $this->mortalityQuery('full', 'DESC', 'MARR'); |
||
3859 | } |
||
3860 | |||
3861 | /** |
||
3862 | * Find the year of the latest marriage. |
||
3863 | * |
||
3864 | * @return string |
||
3865 | */ |
||
3866 | public function lastMarriageYear() |
||
3867 | { |
||
3868 | return $this->mortalityQuery('year', 'DESC', 'MARR'); |
||
3869 | } |
||
3870 | |||
3871 | /** |
||
3872 | * Find the names of spouses of the latest marriage. |
||
3873 | * |
||
3874 | * @return string |
||
3875 | */ |
||
3876 | public function lastMarriageName() |
||
3877 | { |
||
3878 | return $this->mortalityQuery('name', 'DESC', 'MARR'); |
||
3879 | } |
||
3880 | |||
3881 | /** |
||
3882 | * Find the location of the latest marriage. |
||
3883 | * |
||
3884 | * @return string |
||
3885 | */ |
||
3886 | public function lastMarriagePlace() |
||
3887 | { |
||
3888 | return $this->mortalityQuery('place', 'DESC', 'MARR'); |
||
3889 | } |
||
3890 | |||
3891 | /** |
||
3892 | * General query on marriages. |
||
3893 | * |
||
3894 | * @param string[] $params |
||
3895 | * |
||
3896 | * @return string |
||
3897 | */ |
||
3898 | public function statsMarr($params = array()) |
||
3899 | { |
||
3900 | return $this->statsMarrQuery(true, false, -1, -1, $params); |
||
3901 | } |
||
3902 | |||
3903 | /** |
||
3904 | * Find the earliest divorce. |
||
3905 | * |
||
3906 | * @return string |
||
3907 | */ |
||
3908 | public function firstDivorce() |
||
3909 | { |
||
3910 | return $this->mortalityQuery('full', 'ASC', 'DIV'); |
||
3911 | } |
||
3912 | |||
3913 | /** |
||
3914 | * Find the year of the earliest divorce. |
||
3915 | * |
||
3916 | * @return string |
||
3917 | */ |
||
3918 | public function firstDivorceYear() |
||
3919 | { |
||
3920 | return $this->mortalityQuery('year', 'ASC', 'DIV'); |
||
3921 | } |
||
3922 | |||
3923 | /** |
||
3924 | * Find the names of individuals in the earliest divorce. |
||
3925 | * |
||
3926 | * @return string |
||
3927 | */ |
||
3928 | public function firstDivorceName() |
||
3929 | { |
||
3930 | return $this->mortalityQuery('name', 'ASC', 'DIV'); |
||
3931 | } |
||
3932 | |||
3933 | /** |
||
3934 | * Find the location of the earliest divorce. |
||
3935 | * |
||
3936 | * @return string |
||
3937 | */ |
||
3938 | public function firstDivorcePlace() |
||
3939 | { |
||
3940 | return $this->mortalityQuery('place', 'ASC', 'DIV'); |
||
3941 | } |
||
3942 | |||
3943 | /** |
||
3944 | * Find the latest divorce. |
||
3945 | * |
||
3946 | * @return string |
||
3947 | */ |
||
3948 | public function lastDivorce() |
||
3949 | { |
||
3950 | return $this->mortalityQuery('full', 'DESC', 'DIV'); |
||
3951 | } |
||
3952 | |||
3953 | /** |
||
3954 | * Find the year of the latest divorce. |
||
3955 | * |
||
3956 | * @return string |
||
3957 | */ |
||
3958 | public function lastDivorceYear() |
||
3959 | { |
||
3960 | return $this->mortalityQuery('year', 'DESC', 'DIV'); |
||
3961 | } |
||
3962 | |||
3963 | /** |
||
3964 | * Find the names of the individuals in the latest divorce. |
||
3965 | * |
||
3966 | * @return string |
||
3967 | */ |
||
3968 | public function lastDivorceName() |
||
3969 | { |
||
3970 | return $this->mortalityQuery('name', 'DESC', 'DIV'); |
||
3971 | } |
||
3972 | |||
3973 | /** |
||
3974 | * Find the location of the latest divorce. |
||
3975 | * |
||
3976 | * @return string |
||
3977 | */ |
||
3978 | public function lastDivorcePlace() |
||
3979 | { |
||
3980 | return $this->mortalityQuery('place', 'DESC', 'DIV'); |
||
3981 | } |
||
3982 | |||
3983 | /** |
||
3984 | * General divorce query. |
||
3985 | * |
||
3986 | * @param string[] $params |
||
3987 | * |
||
3988 | * @return string |
||
3989 | */ |
||
3990 | public function statsDiv($params = array()) |
||
3991 | { |
||
3992 | return $this->statsDivQuery(true, false, -1, -1, $params); |
||
3993 | } |
||
3994 | |||
3995 | /** |
||
3996 | * General query on ages at marriage. |
||
3997 | * |
||
3998 | * @param bool $simple |
||
3999 | * @param string $sex |
||
4000 | * @param int $year1 |
||
4001 | * @param int $year2 |
||
4002 | * @param string[] $params |
||
4003 | * |
||
4004 | * @return array|string |
||
4005 | */ |
||
4006 | public function statsMarrAgeQuery($simple = true, $sex = 'M', $year1 = -1, $year2 = -1, $params = array()) |
||
4007 | { |
||
4008 | if ($simple) { |
||
4009 | if (isset($params[0]) && $params[0] != '') { |
||
4010 | $size = strtolower($params[0]); |
||
4011 | } else { |
||
4012 | $size = '200x250'; |
||
4013 | } |
||
4014 | $sizes = explode('x', $size); |
||
4015 | $rows = $this->runSql( |
||
4016 | "SELECT " . |
||
4017 | " ROUND(AVG(married.d_julianday2-birth.d_julianday1-182.5)/365.25,1) AS age, " . |
||
4018 | " FLOOR(married.d_year/100+1) AS century, " . |
||
4019 | " 'M' AS sex " . |
||
4020 | "FROM `##dates` AS married " . |
||
4021 | "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . |
||
4022 | "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " . |
||
4023 | "WHERE " . |
||
4024 | " '{$sex}' IN ('M', 'BOTH') AND " . |
||
4025 | " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . |
||
4026 | " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . |
||
4027 | " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " . |
||
4028 | "GROUP BY century, sex " . |
||
4029 | "UNION ALL " . |
||
4030 | "SELECT " . |
||
4031 | " ROUND(AVG(married.d_julianday2-birth.d_julianday1-182.5)/365.25,1) AS age, " . |
||
4032 | " FLOOR(married.d_year/100+1) AS century, " . |
||
4033 | " 'F' AS sex " . |
||
4034 | "FROM `##dates` AS married " . |
||
4035 | "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . |
||
4036 | "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " . |
||
4037 | "WHERE " . |
||
4038 | " '{$sex}' IN ('F', 'BOTH') AND " . |
||
4039 | " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . |
||
4040 | " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . |
||
4041 | " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " . |
||
4042 | " GROUP BY century, sex ORDER BY century" |
||
4043 | ); |
||
4044 | if (empty($rows)) { |
||
4045 | return ''; |
||
4046 | } |
||
4047 | $max = 0; |
||
4048 | foreach ($rows as $values) { |
||
4049 | if ($max < $values['age']) { |
||
4050 | $max = $values['age']; |
||
4051 | } |
||
4052 | } |
||
4053 | $chxl = '0:|'; |
||
4054 | $chmm = ''; |
||
4055 | $chmf = ''; |
||
4056 | $i = 0; |
||
4057 | $countsm = ''; |
||
4058 | $countsf = ''; |
||
4059 | $countsa = ''; |
||
4060 | $out = array(); |
||
4061 | foreach ($rows as $values) { |
||
4062 | $out[$values['century']][$values['sex']] = $values['age']; |
||
4063 | } |
||
4064 | foreach ($out as $century => $values) { |
||
4065 | if ($sizes[0] < 1000) { |
||
4066 | $sizes[0] += 50; |
||
4067 | } |
||
4068 | $chxl .= $this->centuryName($century) . '|'; |
||
4069 | $average = 0; |
||
4070 | if (isset($values['F'])) { |
||
4071 | if ($max <= 50) { |
||
4072 | $value = $values['F'] * 2; |
||
4073 | } else { |
||
4074 | $value = $values['F']; |
||
4075 | } |
||
4076 | $countsf .= $value . ','; |
||
4077 | $average = $value; |
||
4078 | $chmf .= 't' . $values['F'] . ',000000,1,' . $i . ',11,1|'; |
||
4079 | } else { |
||
4080 | $countsf .= '0,'; |
||
4081 | $chmf .= 't0,000000,1,' . $i . ',11,1|'; |
||
4082 | } |
||
4083 | if (isset($values['M'])) { |
||
4084 | if ($max <= 50) { |
||
4085 | $value = $values['M'] * 2; |
||
4086 | } else { |
||
4087 | $value = $values['M']; |
||
4088 | } |
||
4089 | $countsm .= $value . ','; |
||
4090 | if ($average == 0) { |
||
4091 | $countsa .= $value . ','; |
||
4092 | } else { |
||
4093 | $countsa .= (($value + $average) / 2) . ','; |
||
4094 | } |
||
4095 | $chmm .= 't' . $values['M'] . ',000000,0,' . $i . ',11,1|'; |
||
4096 | } else { |
||
4097 | $countsm .= '0,'; |
||
4098 | if ($average == 0) { |
||
4099 | $countsa .= '0,'; |
||
4100 | } else { |
||
4101 | $countsa .= $value . ','; |
||
4102 | } |
||
4103 | $chmm .= 't0,000000,0,' . $i . ',11,1|'; |
||
4104 | } |
||
4105 | $i++; |
||
4106 | } |
||
4107 | $countsm = substr($countsm, 0, -1); |
||
4108 | $countsf = substr($countsf, 0, -1); |
||
4109 | $countsa = substr($countsa, 0, -1); |
||
4110 | $chmf = substr($chmf, 0, -1); |
||
4111 | $chd = 't2:' . $countsm . '|' . $countsf . '|' . $countsa; |
||
4112 | if ($max <= 50) { |
||
4113 | $chxl .= '1:||' . I18N::translate('century') . '|2:|0|10|20|30|40|50|3:||' . I18N::translate('Age') . '|'; |
||
4114 | } else { |
||
4115 | $chxl .= '1:||' . I18N::translate('century') . '|2:|0|10|20|30|40|50|60|70|80|90|100|3:||' . I18N::translate('Age') . '|'; |
||
4116 | } |
||
4117 | if (count($rows) > 4 || mb_strlen(I18N::translate('Average age in century of marriage')) < 30) { |
||
4118 | $chtt = I18N::translate('Average age in century of marriage'); |
||
4119 | } else { |
||
4120 | $offset = 0; |
||
4121 | $counter = array(); |
||
4122 | while ($offset = strpos(I18N::translate('Average age in century of marriage'), ' ', $offset + 1)) { |
||
4123 | $counter[] = $offset; |
||
4124 | } |
||
4125 | $half = (int) (count($counter) / 2); |
||
4126 | $chtt = substr_replace(I18N::translate('Average age in century of marriage'), '|', $counter[$half], 1); |
||
4127 | } |
||
4128 | |||
4129 | return "<img src=\"" . "https://chart.googleapis.com/chart?cht=bvg&chs={$sizes[0]}x{$sizes[1]}&chm=D,FF0000,2,0,3,1|{$chmm}{$chmf}&chf=bg,s,ffffff00|c,s,ffffff00&chtt=" . rawurlencode($chtt) . "&chd={$chd}&chco=0000FF,FFA0CB,FF0000&chbh=20,3&chxt=x,x,y,y&chxl=" . rawurlencode($chxl) . "&chdl=" . rawurlencode(I18N::translate('Males') . "|" . I18N::translate('Females') . "|" . I18N::translate('Average age')) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average age in century of marriage') . "\" title=\"" . I18N::translate('Average age in century of marriage') . "\" />"; |
||
4130 | } else { |
||
4131 | if ($year1 >= 0 && $year2 >= 0) { |
||
4132 | $years = " married.d_year BETWEEN {$year1} AND {$year2} AND "; |
||
4133 | } else { |
||
4134 | $years = ''; |
||
4135 | } |
||
4136 | $rows = $this->runSql( |
||
4137 | "SELECT " . |
||
4138 | " fam.f_id, " . |
||
4139 | " birth.d_gid, " . |
||
4140 | " married.d_julianday2-birth.d_julianday1 AS age " . |
||
4141 | "FROM `##dates` AS married " . |
||
4142 | "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . |
||
4143 | "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " . |
||
4144 | "WHERE " . |
||
4145 | " '{$sex}' IN ('M', 'BOTH') AND {$years} " . |
||
4146 | " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . |
||
4147 | " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . |
||
4148 | " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " . |
||
4149 | "UNION ALL " . |
||
4150 | "SELECT " . |
||
4151 | " fam.f_id, " . |
||
4152 | " birth.d_gid, " . |
||
4153 | " married.d_julianday2-birth.d_julianday1 AS age " . |
||
4154 | "FROM `##dates` AS married " . |
||
4155 | "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . |
||
4156 | "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " . |
||
4157 | "WHERE " . |
||
4158 | " '{$sex}' IN ('F', 'BOTH') AND {$years} " . |
||
4159 | " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . |
||
4160 | " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . |
||
4161 | " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " |
||
4162 | ); |
||
4163 | |||
4164 | return $rows; |
||
4165 | } |
||
4166 | } |
||
4167 | |||
4168 | /** |
||
4169 | * Find the youngest wife. |
||
4170 | * |
||
4171 | * @return string |
||
4172 | */ |
||
4173 | public function youngestMarriageFemale() |
||
4174 | { |
||
4175 | return $this->marriageQuery('full', 'ASC', 'F', false); |
||
4176 | } |
||
4177 | |||
4178 | /** |
||
4179 | * Find the name of the youngest wife. |
||
4180 | * |
||
4181 | * @return string |
||
4182 | */ |
||
4183 | public function youngestMarriageFemaleName() |
||
4184 | { |
||
4185 | return $this->marriageQuery('name', 'ASC', 'F', false); |
||
4186 | } |
||
4187 | |||
4188 | /** |
||
4189 | * Find the age of the youngest wife. |
||
4190 | * |
||
4191 | * @param bool $show_years |
||
4192 | * |
||
4193 | * @return string |
||
4194 | */ |
||
4195 | public function youngestMarriageFemaleAge($show_years = false) |
||
4196 | { |
||
4197 | return $this->marriageQuery('age', 'ASC', 'F', $show_years); |
||
4198 | } |
||
4199 | |||
4200 | /** |
||
4201 | * Find the oldest wife. |
||
4202 | * |
||
4203 | * @return string |
||
4204 | */ |
||
4205 | public function oldestMarriageFemale() |
||
4206 | { |
||
4207 | return $this->marriageQuery('full', 'DESC', 'F', false); |
||
4208 | } |
||
4209 | |||
4210 | /** |
||
4211 | * Find the name of the oldest wife. |
||
4212 | * |
||
4213 | * @return string |
||
4214 | */ |
||
4215 | public function oldestMarriageFemaleName() |
||
4216 | { |
||
4217 | return $this->marriageQuery('name', 'DESC', 'F', false); |
||
4218 | } |
||
4219 | |||
4220 | /** |
||
4221 | * Find the age of the oldest wife. |
||
4222 | * |
||
4223 | * @param bool $show_years |
||
4224 | * |
||
4225 | * @return string |
||
4226 | */ |
||
4227 | public function oldestMarriageFemaleAge($show_years = false) |
||
4228 | { |
||
4229 | return $this->marriageQuery('age', 'DESC', 'F', $show_years); |
||
4230 | } |
||
4231 | |||
4232 | /** |
||
4233 | * Find the youngest husband. |
||
4234 | * |
||
4235 | * @return string |
||
4236 | */ |
||
4237 | public function youngestMarriageMale() |
||
4238 | { |
||
4239 | return $this->marriageQuery('full', 'ASC', 'M', false); |
||
4240 | } |
||
4241 | |||
4242 | /** |
||
4243 | * Find the name of the youngest husband. |
||
4244 | * |
||
4245 | * @return string |
||
4246 | */ |
||
4247 | public function youngestMarriageMaleName() |
||
4248 | { |
||
4249 | return $this->marriageQuery('name', 'ASC', 'M', false); |
||
4250 | } |
||
4251 | |||
4252 | /** |
||
4253 | * Find the age of the youngest husband. |
||
4254 | * |
||
4255 | * @param bool $show_years |
||
4256 | * |
||
4257 | * @return string |
||
4258 | */ |
||
4259 | public function youngestMarriageMaleAge($show_years = false) |
||
4260 | { |
||
4261 | return $this->marriageQuery('age', 'ASC', 'M', $show_years); |
||
4262 | } |
||
4263 | |||
4264 | /** |
||
4265 | * Find the oldest husband. |
||
4266 | * |
||
4267 | * @return string |
||
4268 | */ |
||
4269 | public function oldestMarriageMale() |
||
4270 | { |
||
4271 | return $this->marriageQuery('full', 'DESC', 'M', false); |
||
4272 | } |
||
4273 | |||
4274 | /** |
||
4275 | * Find the name of the oldest husband. |
||
4276 | * |
||
4277 | * @return string |
||
4278 | */ |
||
4279 | public function oldestMarriageMaleName() |
||
4280 | { |
||
4281 | return $this->marriageQuery('name', 'DESC', 'M', false); |
||
4282 | } |
||
4283 | |||
4284 | /** |
||
4285 | * Find the age of the oldest husband. |
||
4286 | * |
||
4287 | * @param bool $show_years |
||
4288 | * |
||
4289 | * @return string |
||
4290 | */ |
||
4291 | public function oldestMarriageMaleAge($show_years = false) |
||
4292 | { |
||
4293 | return $this->marriageQuery('age', 'DESC', 'M', $show_years); |
||
4294 | } |
||
4295 | |||
4296 | /** |
||
4297 | * General query on marriage ages. |
||
4298 | * |
||
4299 | * @param string[] $params |
||
4300 | * |
||
4301 | * @return string |
||
4302 | */ |
||
4303 | public function statsMarrAge($params = array()) |
||
4304 | { |
||
4305 | return $this->statsMarrAgeQuery(true, 'BOTH', -1, -1, $params); |
||
4306 | } |
||
4307 | |||
4308 | /** |
||
4309 | * Find the age between husband and wife. |
||
4310 | * |
||
4311 | * @param string[] $params |
||
4312 | * |
||
4313 | * @return string |
||
4314 | */ |
||
4315 | public function ageBetweenSpousesMF($params = array()) |
||
4316 | { |
||
4317 | return $this->ageBetweenSpousesQuery('nolist', 'DESC', $params); |
||
4318 | } |
||
4319 | |||
4320 | /** |
||
4321 | * Find the age between husband and wife. |
||
4322 | * |
||
4323 | * @param string[] $params |
||
4324 | * |
||
4325 | * @return string |
||
4326 | */ |
||
4327 | public function ageBetweenSpousesMFList($params = array()) |
||
4328 | { |
||
4329 | return $this->ageBetweenSpousesQuery('list', 'DESC', $params); |
||
4330 | } |
||
4331 | |||
4332 | /** |
||
4333 | * Find the age between wife and husband.. |
||
4334 | * |
||
4335 | * @param string[] $params |
||
4336 | * |
||
4337 | * @return string |
||
4338 | */ |
||
4339 | public function ageBetweenSpousesFM($params = array()) |
||
4340 | { |
||
4341 | return $this->ageBetweenSpousesQuery('nolist', 'ASC', $params); |
||
4342 | } |
||
4343 | |||
4344 | /** |
||
4345 | * Find the age between wife and husband.. |
||
4346 | * |
||
4347 | * @param string[] $params |
||
4348 | * |
||
4349 | * @return string |
||
4350 | */ |
||
4351 | public function ageBetweenSpousesFMList($params = array()) |
||
4352 | { |
||
4353 | return $this->ageBetweenSpousesQuery('list', 'ASC', $params); |
||
4354 | } |
||
4355 | |||
4356 | /** |
||
4357 | * General query on marriage ages. |
||
4358 | * |
||
4359 | * @return string |
||
4360 | */ |
||
4361 | public function topAgeOfMarriageFamily() |
||
4362 | { |
||
4363 | return $this->ageOfMarriageQuery('name', 'DESC', array('1')); |
||
4364 | } |
||
4365 | |||
4366 | /** |
||
4367 | * General query on marriage ages. |
||
4368 | * |
||
4369 | * @return string |
||
4370 | */ |
||
4371 | public function topAgeOfMarriage() |
||
4372 | { |
||
4373 | return $this->ageOfMarriageQuery('age', 'DESC', array('1')); |
||
4374 | } |
||
4375 | |||
4376 | /** |
||
4377 | * General query on marriage ages. |
||
4378 | * |
||
4379 | * @param string[] $params |
||
4380 | * |
||
4381 | * @return string |
||
4382 | */ |
||
4383 | public function topAgeOfMarriageFamilies($params = array()) |
||
4384 | { |
||
4385 | return $this->ageOfMarriageQuery('nolist', 'DESC', $params); |
||
4386 | } |
||
4387 | |||
4388 | /** |
||
4389 | * General query on marriage ages. |
||
4390 | * |
||
4391 | * @param string[] $params |
||
4392 | * |
||
4393 | * @return string |
||
4394 | */ |
||
4395 | public function topAgeOfMarriageFamiliesList($params = array()) |
||
4396 | { |
||
4397 | return $this->ageOfMarriageQuery('list', 'DESC', $params); |
||
4398 | } |
||
4399 | |||
4400 | /** |
||
4401 | * General query on marriage ages. |
||
4402 | * |
||
4403 | * @return string |
||
4404 | */ |
||
4405 | public function minAgeOfMarriageFamily() |
||
4406 | { |
||
4407 | return $this->ageOfMarriageQuery('name', 'ASC', array('1')); |
||
4408 | } |
||
4409 | |||
4410 | /** |
||
4411 | * General query on marriage ages. |
||
4412 | * |
||
4413 | * @return string |
||
4414 | */ |
||
4415 | public function minAgeOfMarriage() |
||
4416 | { |
||
4417 | return $this->ageOfMarriageQuery('age', 'ASC', array('1')); |
||
4418 | } |
||
4419 | |||
4420 | /** |
||
4421 | * General query on marriage ages. |
||
4422 | * |
||
4423 | * @param string[] $params |
||
4424 | * |
||
4425 | * @return string |
||
4426 | */ |
||
4427 | public function minAgeOfMarriageFamilies($params = array()) |
||
4428 | { |
||
4429 | return $this->ageOfMarriageQuery('nolist', 'ASC', $params); |
||
4430 | } |
||
4431 | |||
4432 | /** |
||
4433 | * General query on marriage ages. |
||
4434 | * |
||
4435 | * @param string[] $params |
||
4436 | * |
||
4437 | * @return string |
||
4438 | */ |
||
4439 | public function minAgeOfMarriageFamiliesList($params = array()) |
||
4440 | { |
||
4441 | return $this->ageOfMarriageQuery('list', 'ASC', $params); |
||
4442 | } |
||
4443 | |||
4444 | /** |
||
4445 | * Find the youngest mother |
||
4446 | * |
||
4447 | * @return string |
||
4448 | */ |
||
4449 | public function youngestMother() |
||
4450 | { |
||
4451 | return $this->parentsQuery('full', 'ASC', 'F'); |
||
4452 | } |
||
4453 | |||
4454 | /** |
||
4455 | * Find the name of the youngest mother. |
||
4456 | * |
||
4457 | * @return string |
||
4458 | */ |
||
4459 | public function youngestMotherName() |
||
4460 | { |
||
4461 | return $this->parentsQuery('name', 'ASC', 'F'); |
||
4462 | } |
||
4463 | |||
4464 | /** |
||
4465 | * Find the age of the youngest mother. |
||
4466 | * |
||
4467 | * @param bool $show_years |
||
4468 | * |
||
4469 | * @return string |
||
4470 | */ |
||
4471 | public function youngestMotherAge($show_years = false) |
||
4472 | { |
||
4473 | return $this->parentsQuery('age', 'ASC', 'F', $show_years); |
||
4474 | } |
||
4475 | |||
4476 | /** |
||
4477 | * Find the oldest mother. |
||
4478 | * |
||
4479 | * @return string |
||
4480 | */ |
||
4481 | public function oldestMother() |
||
4482 | { |
||
4483 | return $this->parentsQuery('full', 'DESC', 'F'); |
||
4484 | } |
||
4485 | |||
4486 | /** |
||
4487 | * Find the name of the oldest mother. |
||
4488 | * |
||
4489 | * @return string |
||
4490 | */ |
||
4491 | public function oldestMotherName() |
||
4492 | { |
||
4493 | return $this->parentsQuery('name', 'DESC', 'F'); |
||
4494 | } |
||
4495 | |||
4496 | /** |
||
4497 | * Find the age of the oldest mother. |
||
4498 | * |
||
4499 | * @param bool $show_years |
||
4500 | * |
||
4501 | * @return string |
||
4502 | */ |
||
4503 | public function oldestMotherAge($show_years = false) |
||
4504 | { |
||
4505 | return $this->parentsQuery('age', 'DESC', 'F', $show_years); |
||
4506 | } |
||
4507 | |||
4508 | /** |
||
4509 | * Find the youngest father. |
||
4510 | * |
||
4511 | * @return string |
||
4512 | */ |
||
4513 | public function youngestFather() |
||
4514 | { |
||
4515 | return $this->parentsQuery('full', 'ASC', 'M'); |
||
4516 | } |
||
4517 | |||
4518 | /** |
||
4519 | * Find the name of the youngest father. |
||
4520 | * |
||
4521 | * @return string |
||
4522 | */ |
||
4523 | public function youngestFatherName() |
||
4524 | { |
||
4525 | return $this->parentsQuery('name', 'ASC', 'M'); |
||
4526 | } |
||
4527 | |||
4528 | /** |
||
4529 | * Find the age of the youngest father. |
||
4530 | * |
||
4531 | * @param bool $show_years |
||
4532 | * |
||
4533 | * @return string |
||
4534 | */ |
||
4535 | public function youngestFatherAge($show_years = false) |
||
4536 | { |
||
4537 | return $this->parentsQuery('age', 'ASC', 'M', $show_years); |
||
4538 | } |
||
4539 | |||
4540 | /** |
||
4541 | * Find the oldest father. |
||
4542 | * |
||
4543 | * @return string |
||
4544 | */ |
||
4545 | public function oldestFather() |
||
4546 | { |
||
4547 | return $this->parentsQuery('full', 'DESC', 'M'); |
||
4548 | } |
||
4549 | |||
4550 | /** |
||
4551 | * Find the name of the oldest father. |
||
4552 | * |
||
4553 | * @return string |
||
4554 | */ |
||
4555 | public function oldestFatherName() |
||
4556 | { |
||
4557 | return $this->parentsQuery('name', 'DESC', 'M'); |
||
4558 | } |
||
4559 | |||
4560 | /** |
||
4561 | * Find the age of the oldest father. |
||
4562 | * |
||
4563 | * @param bool $show_years |
||
4564 | * |
||
4565 | * @return string |
||
4566 | */ |
||
4567 | public function oldestFatherAge($show_years = false) |
||
4568 | { |
||
4569 | return $this->parentsQuery('age', 'DESC', 'M', $show_years); |
||
4570 | } |
||
4571 | |||
4572 | /** |
||
4573 | * Number of husbands. |
||
4574 | * |
||
4575 | * @return string |
||
4576 | */ |
||
4577 | public function totalMarriedMales() |
||
4578 | { |
||
4579 | $n = Database::prepare("SELECT COUNT(DISTINCT f_husb) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'") |
||
4580 | ->execute(array($this->tree->getTreeId())) |
||
4581 | ->fetchOne(); |
||
4582 | |||
4583 | return I18N::number($n); |
||
4584 | } |
||
4585 | |||
4586 | /** |
||
4587 | * Number of wives. |
||
4588 | * |
||
4589 | * @return string |
||
4590 | */ |
||
4591 | public function totalMarriedFemales() |
||
4592 | { |
||
4593 | $n = Database::prepare("SELECT COUNT(DISTINCT f_wife) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'") |
||
4594 | ->execute(array($this->tree->getTreeId())) |
||
4595 | ->fetchOne(); |
||
4596 | |||
4597 | return I18N::number($n); |
||
4598 | } |
||
4599 | |||
4600 | /** |
||
4601 | * General query on family. |
||
4602 | * |
||
4603 | * @param string $type |
||
4604 | * |
||
4605 | * @return string |
||
4606 | */ |
||
4607 | private function familyQuery($type = 'full') |
||
4608 | { |
||
4609 | $rows = $this->runSql( |
||
4610 | " SELECT f_numchil AS tot, f_id AS id" . |
||
4611 | " FROM `##families`" . |
||
4612 | " WHERE" . |
||
4613 | " f_file={$this->tree->getTreeId()}" . |
||
4614 | " AND f_numchil = (" . |
||
4615 | " SELECT max( f_numchil )" . |
||
4616 | " FROM `##families`" . |
||
4617 | " WHERE f_file ={$this->tree->getTreeId()}" . |
||
4618 | " )" . |
||
4619 | " LIMIT 1" |
||
4620 | ); |
||
4621 | if (!isset($rows[0])) { |
||
4622 | return ''; |
||
4623 | } |
||
4624 | $row = $rows[0]; |
||
4625 | $family = Family::getInstance($row['id'], $this->tree); |
||
4626 | switch ($type) { |
||
4627 | default: |
||
4628 | case 'full': |
||
4629 | if ($family->canShow()) { |
||
4630 | $result = $family->formatList('span', false, $family->getFullName()); |
||
4631 | } else { |
||
4632 | $result = I18N::translate('This information is private and cannot be shown.'); |
||
4633 | } |
||
4634 | break; |
||
4635 | case 'size': |
||
4636 | $result = I18N::number($row['tot']); |
||
4637 | break; |
||
4638 | case 'name': |
||
4639 | $result = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . '</a>'; |
||
4640 | break; |
||
4641 | } |
||
4642 | |||
4643 | return $result; |
||
4644 | } |
||
4645 | |||
4646 | /** |
||
4647 | * General query on families. |
||
4648 | * |
||
4649 | * @param string $type |
||
4650 | * @param string[] $params |
||
4651 | * |
||
4652 | * @return string |
||
4653 | */ |
||
4654 | private function topTenFamilyQuery($type = 'list', $params = array()) |
||
4655 | { |
||
4656 | if (isset($params[0])) { |
||
4657 | $total = (int) $params[0]; |
||
4658 | } else { |
||
4659 | $total = 10; |
||
4660 | } |
||
4661 | $rows = $this->runSql( |
||
4662 | "SELECT f_numchil AS tot, f_id AS id" . |
||
4663 | " FROM `##families`" . |
||
4664 | " WHERE" . |
||
4665 | " f_file={$this->tree->getTreeId()}" . |
||
4666 | " ORDER BY tot DESC" . |
||
4667 | " LIMIT " . $total |
||
4668 | ); |
||
4669 | if (!isset($rows[0])) { |
||
4670 | return ''; |
||
4671 | } |
||
4672 | if (count($rows) < $total) { |
||
4673 | $total = count($rows); |
||
4674 | } |
||
4675 | $top10 = array(); |
||
4676 | for ($c = 0; $c < $total; $c++) { |
||
4677 | $family = Family::getInstance($rows[$c]['id'], $this->tree); |
||
4678 | if ($family->canShow()) { |
||
4679 | if ($type === 'list') { |
||
4680 | $top10[] = |
||
4681 | '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' . |
||
4682 | I18N::plural('%s child', '%s children', $rows[$c]['tot'], I18N::number($rows[$c]['tot'])); |
||
4683 | } else { |
||
4684 | $top10[] = |
||
4685 | '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' . |
||
4686 | I18N::plural('%s child', '%s children', $rows[$c]['tot'], I18N::number($rows[$c]['tot'])); |
||
4687 | } |
||
4688 | } |
||
4689 | } |
||
4690 | if ($type === 'list') { |
||
4691 | $top10 = implode('', $top10); |
||
4692 | } else { |
||
4693 | $top10 = implode('; ', $top10); |
||
4694 | } |
||
4695 | if (I18N::direction() === 'rtl') { |
||
4696 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
4697 | } |
||
4698 | if ($type === 'list') { |
||
4699 | return '<ul>' . $top10 . '</ul>'; |
||
4700 | } |
||
4701 | |||
4702 | return $top10; |
||
4703 | } |
||
4704 | |||
4705 | /** |
||
4706 | * Find the ages between siblings. |
||
4707 | * |
||
4708 | * @param string $type |
||
4709 | * @param string[] $params |
||
4710 | * |
||
4711 | * @return string |
||
4712 | */ |
||
4713 | private function ageBetweenSiblingsQuery($type = 'list', $params = array()) |
||
4714 | { |
||
4715 | if (isset($params[0])) { |
||
4716 | $total = (int) $params[0]; |
||
4717 | } else { |
||
4718 | $total = 10; |
||
4719 | } |
||
4720 | if (isset($params[1])) { |
||
4721 | $one = $params[1]; |
||
4722 | } else { |
||
4723 | $one = false; |
||
4724 | } // each family only once if true |
||
4725 | $rows = $this->runSql( |
||
4726 | " SELECT DISTINCT" . |
||
4727 | " link1.l_from AS family," . |
||
4728 | " link1.l_to AS ch1," . |
||
4729 | " link2.l_to AS ch2," . |
||
4730 | " child1.d_julianday2-child2.d_julianday2 AS age" . |
||
4731 | " FROM `##link` AS link1" . |
||
4732 | " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" . |
||
4733 | " LEFT JOIN `##dates` AS child2 ON child2.d_file = {$this->tree->getTreeId()}" . |
||
4734 | " LEFT JOIN `##link` AS link2 ON link2.l_file = {$this->tree->getTreeId()}" . |
||
4735 | " WHERE" . |
||
4736 | " link1.l_file = {$this->tree->getTreeId()} AND" . |
||
4737 | " link1.l_from = link2.l_from AND" . |
||
4738 | " link1.l_type = 'CHIL' AND" . |
||
4739 | " child1.d_gid = link1.l_to AND" . |
||
4740 | " child1.d_fact = 'BIRT' AND" . |
||
4741 | " link2.l_type = 'CHIL' AND" . |
||
4742 | " child2.d_gid = link2.l_to AND" . |
||
4743 | " child2.d_fact = 'BIRT' AND" . |
||
4744 | " child1.d_julianday2 > child2.d_julianday2 AND" . |
||
4745 | " child2.d_julianday2 <> 0 AND" . |
||
4746 | " child1.d_gid <> child2.d_gid" . |
||
4747 | " ORDER BY age DESC" . |
||
4748 | " LIMIT " . $total |
||
4749 | ); |
||
4750 | if (!isset($rows[0])) { |
||
4751 | return ''; |
||
4752 | } |
||
4753 | $top10 = array(); |
||
4754 | $dist = array(); |
||
4755 | foreach ($rows as $fam) { |
||
4756 | $family = Family::getInstance($fam['family'], $this->tree); |
||
4757 | $child1 = Individual::getInstance($fam['ch1'], $this->tree); |
||
4758 | $child2 = Individual::getInstance($fam['ch2'], $this->tree); |
||
4759 | if ($type == 'name') { |
||
4760 | if ($child1->canShow() && $child2->canShow()) { |
||
4761 | $return = '<a href="' . $child2->getHtmlUrl() . '">' . $child2->getFullName() . '</a> '; |
||
4762 | $return .= I18N::translate('and') . ' '; |
||
4763 | $return .= '<a href="' . $child1->getHtmlUrl() . '">' . $child1->getFullName() . '</a>'; |
||
4764 | $return .= ' <a href="' . $family->getHtmlUrl() . '">[' . I18N::translate('View this family') . ']</a>'; |
||
4765 | } else { |
||
4766 | $return = I18N::translate('This information is private and cannot be shown.'); |
||
4767 | } |
||
4768 | |||
4769 | return $return; |
||
4770 | } |
||
4771 | $age = $fam['age']; |
||
4772 | if ((int) ($age / 365.25) > 0) { |
||
4773 | $age = (int) ($age / 365.25) . 'y'; |
||
4774 | } elseif ((int) ($age / 30.4375) > 0) { |
||
4775 | $age = (int) ($age / 30.4375) . 'm'; |
||
4776 | } else { |
||
4777 | $age = $age . 'd'; |
||
4778 | } |
||
4779 | $age = FunctionsDate::getAgeAtEvent($age); |
||
4780 | if ($type == 'age') { |
||
4781 | return $age; |
||
4782 | } |
||
4783 | if ($type == 'list') { |
||
4784 | if ($one && !in_array($fam['family'], $dist)) { |
||
4785 | if ($child1->canShow() && $child2->canShow()) { |
||
4786 | $return = "<li>"; |
||
4787 | $return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> "; |
||
4788 | $return .= I18N::translate('and') . " "; |
||
4789 | $return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>"; |
||
4790 | $return .= " (" . $age . ")"; |
||
4791 | $return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>"; |
||
4792 | $return .= '</li>'; |
||
4793 | $top10[] = $return; |
||
4794 | $dist[] = $fam['family']; |
||
4795 | } |
||
4796 | } elseif (!$one && $child1->canShow() && $child2->canShow()) { |
||
4797 | $return = "<li>"; |
||
4798 | $return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> "; |
||
4799 | $return .= I18N::translate('and') . " "; |
||
4800 | $return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>"; |
||
4801 | $return .= " (" . $age . ")"; |
||
4802 | $return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>"; |
||
4803 | $return .= '</li>'; |
||
4804 | $top10[] = $return; |
||
4805 | } |
||
4806 | } else { |
||
4807 | if ($child1->canShow() && $child2->canShow()) { |
||
4808 | $return = $child2->formatList('span', false, $child2->getFullName()); |
||
4809 | $return .= "<br>" . I18N::translate('and') . "<br>"; |
||
4810 | $return .= $child1->formatList('span', false, $child1->getFullName()); |
||
4811 | $return .= "<br><a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>"; |
||
4812 | |||
4813 | return $return; |
||
4814 | } else { |
||
4815 | return I18N::translate('This information is private and cannot be shown.'); |
||
4816 | } |
||
4817 | } |
||
4818 | } |
||
4819 | if ($type === 'list') { |
||
4820 | $top10 = implode('', $top10); |
||
4821 | } |
||
4822 | if (I18N::direction() === 'rtl') { |
||
4823 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
4824 | } |
||
4825 | if ($type === 'list') { |
||
4826 | return '<ul>' . $top10 . '</ul>'; |
||
4827 | } |
||
4828 | |||
4829 | return $top10; |
||
4830 | } |
||
4831 | |||
4832 | /** |
||
4833 | * Find the month in the year of the birth of the first child. |
||
4834 | * |
||
4835 | * @param bool $simple |
||
4836 | * @param bool $sex |
||
4837 | * @param int $year1 |
||
4838 | * @param int $year2 |
||
4839 | * @param string[] $params |
||
4840 | * |
||
4841 | * @return string|string[][] |
||
4842 | */ |
||
4843 | public function monthFirstChildQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) |
||
4844 | { |
||
4845 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
4846 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
4847 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
4848 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
4849 | |||
4850 | if ($year1 >= 0 && $year2 >= 0) { |
||
4851 | $sql_years = " AND (d_year BETWEEN '{$year1}' AND '{$year2}')"; |
||
4852 | } else { |
||
4853 | $sql_years = ''; |
||
4854 | } |
||
4855 | if ($sex) { |
||
4856 | $sql_sex1 = ', i_sex'; |
||
4857 | $sql_sex2 = " JOIN `##individuals` AS child ON child1.d_file = i_file AND child1.d_gid = child.i_id "; |
||
4858 | } else { |
||
4859 | $sql_sex1 = ''; |
||
4860 | $sql_sex2 = ''; |
||
4861 | } |
||
4862 | $sql = |
||
4863 | "SELECT d_month{$sql_sex1}, COUNT(*) AS total " . |
||
4864 | "FROM (" . |
||
4865 | " SELECT family{$sql_sex1}, MIN(date) AS d_date, d_month" . |
||
4866 | " FROM (" . |
||
4867 | " SELECT" . |
||
4868 | " link1.l_from AS family," . |
||
4869 | " link1.l_to AS child," . |
||
4870 | " child1.d_julianday2 AS date," . |
||
4871 | " child1.d_month as d_month" . |
||
4872 | $sql_sex1 . |
||
4873 | " FROM `##link` AS link1" . |
||
4874 | " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" . |
||
4875 | $sql_sex2 . |
||
4876 | " WHERE" . |
||
4877 | " link1.l_file = {$this->tree->getTreeId()} AND" . |
||
4878 | " link1.l_type = 'CHIL' AND" . |
||
4879 | " child1.d_gid = link1.l_to AND" . |
||
4880 | " child1.d_fact = 'BIRT' AND" . |
||
4881 | " child1.d_month IN ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC')" . |
||
4882 | $sql_years . |
||
4883 | " ORDER BY date" . |
||
4884 | " ) AS children" . |
||
4885 | " GROUP BY family, d_month{$sql_sex1}" . |
||
4886 | ") AS first_child " . |
||
4887 | "GROUP BY d_month"; |
||
4888 | if ($sex) { |
||
4889 | $sql .= ', i_sex'; |
||
4890 | } |
||
4891 | $rows = $this->runSql($sql); |
||
4892 | if ($simple) { |
||
4893 | if (isset($params[0]) && $params[0] != '') { |
||
4894 | $size = strtolower($params[0]); |
||
4895 | } else { |
||
4896 | $size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y; |
||
4897 | } |
||
4898 | if (isset($params[1]) && $params[1] != '') { |
||
4899 | $color_from = strtolower($params[1]); |
||
4900 | } else { |
||
4901 | $color_from = $WT_STATS_CHART_COLOR1; |
||
4902 | } |
||
4903 | if (isset($params[2]) && $params[2] != '') { |
||
4904 | $color_to = strtolower($params[2]); |
||
4905 | } else { |
||
4906 | $color_to = $WT_STATS_CHART_COLOR2; |
||
4907 | } |
||
4908 | $sizes = explode('x', $size); |
||
4909 | $tot = 0; |
||
4910 | foreach ($rows as $values) { |
||
4911 | $tot += $values['total']; |
||
4912 | } |
||
4913 | // Beware divide by zero |
||
4914 | if ($tot == 0) { |
||
4915 | return ''; |
||
4916 | } |
||
4917 | $text = ''; |
||
4918 | $counts = array(); |
||
4919 | foreach ($rows as $values) { |
||
4920 | $counts[] = round(100 * $values['total'] / $tot, 0); |
||
4921 | switch ($values['d_month']) { |
||
4922 | default: |
||
4923 | case 'JAN': |
||
4924 | $values['d_month'] = 1; |
||
4925 | break; |
||
4926 | case 'FEB': |
||
4927 | $values['d_month'] = 2; |
||
4928 | break; |
||
4929 | case 'MAR': |
||
4930 | $values['d_month'] = 3; |
||
4931 | break; |
||
4932 | case 'APR': |
||
4933 | $values['d_month'] = 4; |
||
4934 | break; |
||
4935 | case 'MAY': |
||
4936 | $values['d_month'] = 5; |
||
4937 | break; |
||
4938 | case 'JUN': |
||
4939 | $values['d_month'] = 6; |
||
4940 | break; |
||
4941 | case 'JUL': |
||
4942 | $values['d_month'] = 7; |
||
4943 | break; |
||
4944 | case 'AUG': |
||
4945 | $values['d_month'] = 8; |
||
4946 | break; |
||
4947 | case 'SEP': |
||
4948 | $values['d_month'] = 9; |
||
4949 | break; |
||
4950 | case 'OCT': |
||
4951 | $values['d_month'] = 10; |
||
4952 | break; |
||
4953 | case 'NOV': |
||
4954 | $values['d_month'] = 11; |
||
4955 | break; |
||
4956 | case 'DEC': |
||
4957 | $values['d_month'] = 12; |
||
4958 | break; |
||
4959 | } |
||
4960 | $text .= I18N::translate(ucfirst(strtolower(($values['d_month'])))) . ' - ' . $values['total'] . '|'; |
||
4961 | } |
||
4962 | $chd = $this->arrayToExtendedEncoding($counts); |
||
4963 | $chl = substr($text, 0, -1); |
||
4964 | |||
4965 | return '<img src="https://chart.googleapis.com/chart?cht=p3&chd=e:' . $chd . '&chs=' . $size . '&chco=' . $color_from . ',' . $color_to . '&chf=bg,s,ffffff00&chl=' . $chl . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . I18N::translate('Month of birth of first child in a relation') . '" title="' . I18N::translate('Month of birth of first child in a relation') . '" />'; |
||
4966 | } |
||
4967 | |||
4968 | return $rows; |
||
4969 | } |
||
4970 | |||
4971 | /** |
||
4972 | * Find the family with the most children. |
||
4973 | * |
||
4974 | * @return string |
||
4975 | */ |
||
4976 | public function largestFamily() |
||
4977 | { |
||
4978 | return $this->familyQuery('full'); |
||
4979 | } |
||
4980 | |||
4981 | /** |
||
4982 | * Find the number of children in the largest family. |
||
4983 | * |
||
4984 | * @return string |
||
4985 | */ |
||
4986 | public function largestFamilySize() |
||
4987 | { |
||
4988 | return $this->familyQuery('size'); |
||
4989 | } |
||
4990 | |||
4991 | /** |
||
4992 | * Find the family with the most children. |
||
4993 | * |
||
4994 | * @return string |
||
4995 | */ |
||
4996 | public function largestFamilyName() |
||
4997 | { |
||
4998 | return $this->familyQuery('name'); |
||
4999 | } |
||
5000 | |||
5001 | /** |
||
5002 | * The the families with the most children. |
||
5003 | * |
||
5004 | * @param string[] $params |
||
5005 | * |
||
5006 | * @return string |
||
5007 | */ |
||
5008 | public function topTenLargestFamily($params = array()) |
||
5009 | { |
||
5010 | return $this->topTenFamilyQuery('nolist', $params); |
||
5011 | } |
||
5012 | |||
5013 | /** |
||
5014 | * Find the families with the most children. |
||
5015 | * |
||
5016 | * @param string[] $params |
||
5017 | * |
||
5018 | * @return string |
||
5019 | */ |
||
5020 | public function topTenLargestFamilyList($params = array()) |
||
5021 | { |
||
5022 | return $this->topTenFamilyQuery('list', $params); |
||
5023 | } |
||
5024 | |||
5025 | /** |
||
5026 | * Create a chart of the largest families. |
||
5027 | * |
||
5028 | * @param string[] $params |
||
5029 | * |
||
5030 | * @return string |
||
5031 | */ |
||
5032 | public function chartLargestFamilies($params = array()) |
||
5033 | { |
||
5034 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
5035 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
5036 | $WT_STATS_L_CHART_X = Theme::theme()->parameter('stats-large-chart-x'); |
||
5037 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
5038 | |||
5039 | if (isset($params[0]) && $params[0] != '') { |
||
5040 | $size = strtolower($params[0]); |
||
5041 | } else { |
||
5042 | $size = $WT_STATS_L_CHART_X . 'x' . $WT_STATS_S_CHART_Y; |
||
5043 | } |
||
5044 | if (isset($params[1]) && $params[1] != '') { |
||
5045 | $color_from = strtolower($params[1]); |
||
5046 | } else { |
||
5047 | $color_from = $WT_STATS_CHART_COLOR1; |
||
5048 | } |
||
5049 | if (isset($params[2]) && $params[2] != '') { |
||
5050 | $color_to = strtolower($params[2]); |
||
5051 | } else { |
||
5052 | $color_to = $WT_STATS_CHART_COLOR2; |
||
5053 | } |
||
5054 | if (isset($params[3]) && $params[3] != '') { |
||
5055 | $total = strtolower($params[3]); |
||
5056 | } else { |
||
5057 | $total = 10; |
||
5058 | } |
||
5059 | $sizes = explode('x', $size); |
||
5060 | $total = (int) $total; |
||
5061 | $rows = $this->runSql( |
||
5062 | " SELECT f_numchil AS tot, f_id AS id" . |
||
5063 | " FROM `##families`" . |
||
5064 | " WHERE f_file={$this->tree->getTreeId()}" . |
||
5065 | " ORDER BY tot DESC" . |
||
5066 | " LIMIT " . $total |
||
5067 | ); |
||
5068 | if (!isset($rows[0])) { |
||
5069 | return ''; |
||
5070 | } |
||
5071 | $tot = 0; |
||
5072 | foreach ($rows as $row) { |
||
5073 | $tot += (int) $row['tot']; |
||
5074 | } |
||
5075 | $chd = ''; |
||
5076 | $chl = array(); |
||
5077 | foreach ($rows as $row) { |
||
5078 | $family = Family::getInstance($row['id'], $this->tree); |
||
5079 | if ($family->canShow()) { |
||
5080 | if ($tot == 0) { |
||
5081 | $per = 0; |
||
5082 | } else { |
||
5083 | $per = round(100 * $row['tot'] / $tot, 0); |
||
5084 | } |
||
5085 | $chd .= $this->arrayToExtendedEncoding(array($per)); |
||
5086 | $chl[] = htmlspecialchars_decode(strip_tags($family->getFullName())) . ' - ' . I18N::number($row['tot']); |
||
5087 | } |
||
5088 | } |
||
5089 | $chl = rawurlencode(implode('|', $chl)); |
||
5090 | |||
5091 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Largest families') . "\" title=\"" . I18N::translate('Largest families') . "\" />"; |
||
5092 | } |
||
5093 | |||
5094 | /** |
||
5095 | * Count the total children. |
||
5096 | * |
||
5097 | * @return string |
||
5098 | */ |
||
5099 | public function totalChildren() |
||
5100 | { |
||
5101 | $rows = $this->runSql("SELECT SUM(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}"); |
||
5102 | |||
5103 | return I18N::number($rows[0]['tot']); |
||
5104 | } |
||
5105 | |||
5106 | /** |
||
5107 | * Find the average number of children in families. |
||
5108 | * |
||
5109 | * @return string |
||
5110 | */ |
||
5111 | public function averageChildren() |
||
5112 | { |
||
5113 | $rows = $this->runSql("SELECT AVG(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}"); |
||
5114 | |||
5115 | return I18N::number($rows[0]['tot'], 2); |
||
5116 | } |
||
5117 | |||
5118 | /** |
||
5119 | * General query on familes/children. |
||
5120 | * |
||
5121 | * @param bool $simple |
||
5122 | * @param string $sex |
||
5123 | * @param int $year1 |
||
5124 | * @param int $year2 |
||
5125 | * @param string[] $params |
||
5126 | * |
||
5127 | * @return string|string[][] |
||
5128 | */ |
||
5129 | public function statsChildrenQuery($simple = true, $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) |
||
5130 | { |
||
5131 | if ($simple) { |
||
5132 | if (isset($params[0]) && $params[0] != '') { |
||
5133 | $size = strtolower($params[0]); |
||
5134 | } else { |
||
5135 | $size = '220x200'; |
||
5136 | } |
||
5137 | $sizes = explode('x', $size); |
||
5138 | $max = 0; |
||
5139 | $rows = $this->runSql( |
||
5140 | " SELECT ROUND(AVG(f_numchil),2) AS num, FLOOR(d_year/100+1) AS century" . |
||
5141 | " FROM `##families`" . |
||
5142 | " JOIN `##dates` ON (d_file = f_file AND d_gid=f_id)" . |
||
5143 | " WHERE f_file = {$this->tree->getTreeId()}" . |
||
5144 | " AND d_julianday1<>0" . |
||
5145 | " AND d_fact = 'MARR'" . |
||
5146 | " AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')" . |
||
5147 | " GROUP BY century" . |
||
5148 | " ORDER BY century"); |
||
5149 | if (empty($rows)) { |
||
5150 | return ''; |
||
5151 | } |
||
5152 | foreach ($rows as $values) { |
||
5153 | if ($max < $values['num']) { |
||
5154 | $max = $values['num']; |
||
5155 | } |
||
5156 | } |
||
5157 | $chm = ""; |
||
5158 | $chxl = "0:|"; |
||
5159 | $i = 0; |
||
5160 | $counts = array(); |
||
5161 | foreach ($rows as $values) { |
||
5162 | if ($sizes[0] < 980) { |
||
5163 | $sizes[0] += 38; |
||
5164 | } |
||
5165 | $chxl .= $this->centuryName($values['century']) . "|"; |
||
5166 | if ($max <= 5) { |
||
5167 | $counts[] = round($values['num'] * 819.2 - 1, 1); |
||
5168 | } elseif ($max <= 10) { |
||
5169 | $counts[] = round($values['num'] * 409.6, 1); |
||
5170 | } else { |
||
5171 | $counts[] = round($values['num'] * 204.8, 1); |
||
5172 | } |
||
5173 | $chm .= 't' . $values['num'] . ',000000,0,' . $i . ',11,1|'; |
||
5174 | $i++; |
||
5175 | } |
||
5176 | $chd = $this->arrayToExtendedEncoding($counts); |
||
5177 | $chm = substr($chm, 0, -1); |
||
5178 | if ($max <= 5) { |
||
5179 | $chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|3:||" . I18N::translate('Number of children') . "|"; |
||
5180 | } elseif ($max <= 10) { |
||
5181 | $chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|6|7|8|9|10|3:||" . I18N::translate('Number of children') . "|"; |
||
5182 | } else { |
||
5183 | $chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|3:||" . I18N::translate('Number of children') . "|"; |
||
5184 | } |
||
5185 | |||
5186 | return "<img src=\"https://chart.googleapis.com/chart?cht=bvg&chs={$sizes[0]}x{$sizes[1]}&chf=bg,s,ffffff00|c,s,ffffff00&chm=D,FF0000,0,0,3,1|{$chm}&chd=e:{$chd}&chco=0000FF&chbh=30,3&chxt=x,x,y,y&chxl=" . rawurlencode($chxl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average number of children per family') . "\" title=\"" . I18N::translate('Average number of children per family') . "\" />"; |
||
5187 | } else { |
||
5188 | if ($sex == 'M') { |
||
5189 | $sql = |
||
5190 | "SELECT num, COUNT(*) AS total FROM " . |
||
5191 | "(SELECT count(i_sex) AS num FROM `##link` " . |
||
5192 | "LEFT OUTER JOIN `##individuals` " . |
||
5193 | "ON l_from=i_id AND l_file=i_file AND i_sex='M' AND l_type='FAMC' " . |
||
5194 | "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->getTreeId()} GROUP BY l_to" . |
||
5195 | ") boys" . |
||
5196 | " GROUP BY num" . |
||
5197 | " ORDER BY num"; |
||
5198 | } elseif ($sex == 'F') { |
||
5199 | $sql = |
||
5200 | "SELECT num, COUNT(*) AS total FROM " . |
||
5201 | "(SELECT count(i_sex) AS num FROM `##link` " . |
||
5202 | "LEFT OUTER JOIN `##individuals` " . |
||
5203 | "ON l_from=i_id AND l_file=i_file AND i_sex='F' AND l_type='FAMC' " . |
||
5204 | "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->getTreeId()} GROUP BY l_to" . |
||
5205 | ") girls" . |
||
5206 | " GROUP BY num" . |
||
5207 | " ORDER BY num"; |
||
5208 | } else { |
||
5209 | $sql = "SELECT f_numchil, COUNT(*) AS total FROM `##families` "; |
||
5210 | if ($year1 >= 0 && $year2 >= 0) { |
||
5211 | $sql .= |
||
5212 | "AS fam LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" |
||
5213 | . " WHERE" |
||
5214 | . " married.d_gid = fam.f_id AND" |
||
5215 | . " fam.f_file = {$this->tree->getTreeId()} AND" |
||
5216 | . " married.d_fact = 'MARR' AND" |
||
5217 | . " married.d_year BETWEEN '{$year1}' AND '{$year2}'"; |
||
5218 | } else { |
||
5219 | $sql .= "WHERE f_file={$this->tree->getTreeId()}"; |
||
5220 | } |
||
5221 | $sql .= " GROUP BY f_numchil"; |
||
5222 | } |
||
5223 | $rows = $this->runSql($sql); |
||
5224 | |||
5225 | return $rows; |
||
5226 | } |
||
5227 | } |
||
5228 | |||
5229 | /** |
||
5230 | * Genearl query on families/children. |
||
5231 | * |
||
5232 | * @param string[] $params |
||
5233 | * |
||
5234 | * @return string |
||
5235 | */ |
||
5236 | public function statsChildren($params = array()) |
||
5237 | { |
||
5238 | return $this->statsChildrenQuery(true, 'BOTH', -1, -1, $params); |
||
5239 | } |
||
5240 | |||
5241 | /** |
||
5242 | * Find the names of siblings with the widest age gap. |
||
5243 | * |
||
5244 | * @param string[] $params |
||
5245 | * |
||
5246 | * @return string |
||
5247 | */ |
||
5248 | public function topAgeBetweenSiblingsName($params = array()) |
||
5249 | { |
||
5250 | return $this->ageBetweenSiblingsQuery('name', $params); |
||
5251 | } |
||
5252 | |||
5253 | /** |
||
5254 | * Find the widest age gap between siblings. |
||
5255 | * |
||
5256 | * @param string[] $params |
||
5257 | * |
||
5258 | * @return string |
||
5259 | */ |
||
5260 | public function topAgeBetweenSiblings($params = array()) |
||
5261 | { |
||
5262 | return $this->ageBetweenSiblingsQuery('age', $params); |
||
5263 | } |
||
5264 | |||
5265 | /** |
||
5266 | * Find the name of siblings with the widest age gap. |
||
5267 | * |
||
5268 | * @param string[] $params |
||
5269 | * |
||
5270 | * @return string |
||
5271 | */ |
||
5272 | public function topAgeBetweenSiblingsFullName($params = array()) |
||
5273 | { |
||
5274 | return $this->ageBetweenSiblingsQuery('nolist', $params); |
||
5275 | } |
||
5276 | |||
5277 | /** |
||
5278 | * Find the siblings with the widest age gaps. |
||
5279 | * |
||
5280 | * @param string[] $params |
||
5281 | * |
||
5282 | * @return string |
||
5283 | */ |
||
5284 | public function topAgeBetweenSiblingsList($params = array()) |
||
5285 | { |
||
5286 | return $this->ageBetweenSiblingsQuery('list', $params); |
||
5287 | } |
||
5288 | |||
5289 | /** |
||
5290 | * Find the families with no children. |
||
5291 | * |
||
5292 | * @return string |
||
5293 | */ |
||
5294 | private function noChildrenFamiliesQuery() |
||
5295 | { |
||
5296 | $rows = $this->runSql( |
||
5297 | " SELECT COUNT(*) AS tot" . |
||
5298 | " FROM `##families`" . |
||
5299 | " WHERE f_numchil = 0 AND f_file = {$this->tree->getTreeId()}"); |
||
5300 | |||
5301 | return $rows[0]['tot']; |
||
5302 | } |
||
5303 | |||
5304 | /** |
||
5305 | * Find the families with no children. |
||
5306 | * |
||
5307 | * @return string |
||
5308 | */ |
||
5309 | public function noChildrenFamilies() |
||
5310 | { |
||
5311 | return I18N::number($this->noChildrenFamiliesQuery()); |
||
5312 | } |
||
5313 | |||
5314 | /** |
||
5315 | * Find the families with no children. |
||
5316 | * |
||
5317 | * @param string[] $params |
||
5318 | * |
||
5319 | * @return string |
||
5320 | */ |
||
5321 | public function noChildrenFamiliesList($params = array()) |
||
5322 | { |
||
5323 | if (isset($params[0]) && $params[0] != '') { |
||
5324 | $type = strtolower($params[0]); |
||
5325 | } else { |
||
5326 | $type = 'list'; |
||
5327 | } |
||
5328 | $rows = $this->runSql( |
||
5329 | " SELECT f_id AS family" . |
||
5330 | " FROM `##families` AS fam" . |
||
5331 | " WHERE f_numchil = 0 AND fam.f_file = {$this->tree->getTreeId()}"); |
||
5332 | if (!isset($rows[0])) { |
||
5333 | return ''; |
||
5334 | } |
||
5335 | $top10 = array(); |
||
5336 | foreach ($rows as $row) { |
||
5337 | $family = Family::getInstance($row['family'], $this->tree); |
||
5338 | if ($family->canShow()) { |
||
5339 | if ($type == 'list') { |
||
5340 | $top10[] = "<li><a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a></li>"; |
||
5341 | } else { |
||
5342 | $top10[] = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a>"; |
||
5343 | } |
||
5344 | } |
||
5345 | } |
||
5346 | if ($type == 'list') { |
||
5347 | $top10 = implode('', $top10); |
||
5348 | } else { |
||
5349 | $top10 = implode('; ', $top10); |
||
5350 | } |
||
5351 | if (I18N::direction() === 'rtl') { |
||
5352 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
5353 | } |
||
5354 | if ($type === 'list') { |
||
5355 | return '<ul>' . $top10 . '</ul>'; |
||
5356 | } |
||
5357 | |||
5358 | return $top10; |
||
5359 | } |
||
5360 | |||
5361 | /** |
||
5362 | * Create a chart of children with no families. |
||
5363 | * |
||
5364 | * @param string[] $params |
||
5365 | * |
||
5366 | * @return string |
||
5367 | */ |
||
5368 | public function chartNoChildrenFamilies($params = array()) |
||
5369 | { |
||
5370 | if (isset($params[0]) && $params[0] != '') { |
||
5371 | $size = strtolower($params[0]); |
||
5372 | } else { |
||
5373 | $size = '220x200'; |
||
5374 | } |
||
5375 | if (isset($params[1]) && $params[1] != '') { |
||
5376 | $year1 = $params[1]; |
||
5377 | } else { |
||
5378 | $year1 = -1; |
||
5379 | } |
||
5380 | if (isset($params[2]) && $params[2] != '') { |
||
5381 | $year2 = $params[2]; |
||
5382 | } else { |
||
5383 | $year2 = -1; |
||
5384 | } |
||
5385 | $sizes = explode('x', $size); |
||
5386 | if ($year1 >= 0 && $year2 >= 0) { |
||
5387 | $years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND"; |
||
5388 | } else { |
||
5389 | $years = ""; |
||
5390 | } |
||
5391 | $max = 0; |
||
5392 | $tot = 0; |
||
5393 | $rows = $this->runSql( |
||
5394 | "SELECT" . |
||
5395 | " COUNT(*) AS count," . |
||
5396 | " FLOOR(married.d_year/100+1) AS century" . |
||
5397 | " FROM" . |
||
5398 | " `##families` AS fam" . |
||
5399 | " JOIN" . |
||
5400 | " `##dates` AS married ON (married.d_file = fam.f_file AND married.d_gid = fam.f_id)" . |
||
5401 | " WHERE" . |
||
5402 | " f_numchil = 0 AND" . |
||
5403 | " fam.f_file = {$this->tree->getTreeId()} AND" . |
||
5404 | $years . |
||
5405 | " married.d_fact = 'MARR' AND" . |
||
5406 | " married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')" . |
||
5407 | " GROUP BY century ORDER BY century" |
||
5408 | ); |
||
5409 | if (empty($rows)) { |
||
5410 | return ''; |
||
5411 | } |
||
5412 | foreach ($rows as $values) { |
||
5413 | if ($max < $values['count']) { |
||
5414 | $max = $values['count']; |
||
5415 | } |
||
5416 | $tot += (int) $values['count']; |
||
5417 | } |
||
5418 | $unknown = $this->noChildrenFamiliesQuery() - $tot; |
||
5419 | if ($unknown > $max) { |
||
5420 | $max = $unknown; |
||
5421 | } |
||
5422 | $chm = ""; |
||
5423 | $chxl = "0:|"; |
||
5424 | $i = 0; |
||
5425 | $counts = array(); |
||
5426 | foreach ($rows as $values) { |
||
5427 | if ($sizes[0] < 980) { |
||
5428 | $sizes[0] += 38; |
||
5429 | } |
||
5430 | $chxl .= $this->centuryName($values['century']) . "|"; |
||
5431 | $counts[] = round(4095 * $values['count'] / ($max + 1)); |
||
5432 | $chm .= 't' . $values['count'] . ',000000,0,' . $i . ',11,1|'; |
||
5433 | $i++; |
||
5434 | } |
||
5435 | $counts[] = round(4095 * $unknown / ($max + 1)); |
||
5436 | $chd = $this->arrayToExtendedEncoding($counts); |
||
5437 | $chm .= 't' . $unknown . ',000000,0,' . $i . ',11,1'; |
||
5438 | $chxl .= I18N::translateContext('unknown century', 'Unknown') . "|1:||" . I18N::translate('century') . "|2:|0|"; |
||
5439 | $step = $max + 1; |
||
5440 | for ($d = (int) ($max + 1); $d > 0; $d--) { |
||
5441 | if (($max + 1) < ($d * 10 + 1) && fmod(($max + 1), $d) == 0) { |
||
5442 | $step = $d; |
||
5443 | } |
||
5444 | } |
||
5445 | if ($step == (int) ($max + 1)) { |
||
5446 | for ($d = (int) ($max); $d > 0; $d--) { |
||
5447 | if ($max < ($d * 10 + 1) && fmod($max, $d) == 0) { |
||
5448 | $step = $d; |
||
5449 | } |
||
5450 | } |
||
5451 | } |
||
5452 | for ($n = $step; $n <= ($max + 1); $n += $step) { |
||
5453 | $chxl .= $n . "|"; |
||
5454 | } |
||
5455 | $chxl .= "3:||" . I18N::translate('Total families') . "|"; |
||
5456 | |||
5457 | return "<img src=\"https://chart.googleapis.com/chart?cht=bvg&chs={$sizes[0]}x{$sizes[1]}&chf=bg,s,ffffff00|c,s,ffffff00&chm=D,FF0000,0,0:" . ($i - 1) . ",3,1|{$chm}&chd=e:{$chd}&chco=0000FF,ffffff00&chbh=30,3&chxt=x,x,y,y&chxl=" . rawurlencode($chxl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Number of families without children') . "\" title=\"" . I18N::translate('Number of families without children') . "\" />"; |
||
5458 | } |
||
5459 | |||
5460 | /** |
||
5461 | * Find the couple with the most grandchildren. |
||
5462 | * |
||
5463 | * @param string $type |
||
5464 | * @param string[] $params |
||
5465 | * |
||
5466 | * @return string |
||
5467 | */ |
||
5468 | private function topTenGrandFamilyQuery($type = 'list', $params = array()) |
||
5469 | { |
||
5470 | if (isset($params[0])) { |
||
5471 | $total = (int) $params[0]; |
||
5472 | } else { |
||
5473 | $total = 10; |
||
5474 | } |
||
5475 | $rows = $this->runSql( |
||
5476 | "SELECT COUNT(*) AS tot, f_id AS id" . |
||
5477 | " FROM `##families`" . |
||
5478 | " JOIN `##link` AS children ON children.l_file = {$this->tree->getTreeId()}" . |
||
5479 | " JOIN `##link` AS mchildren ON mchildren.l_file = {$this->tree->getTreeId()}" . |
||
5480 | " JOIN `##link` AS gchildren ON gchildren.l_file = {$this->tree->getTreeId()}" . |
||
5481 | " WHERE" . |
||
5482 | " f_file={$this->tree->getTreeId()} AND" . |
||
5483 | " children.l_from=f_id AND" . |
||
5484 | " children.l_type='CHIL' AND" . |
||
5485 | " children.l_to=mchildren.l_from AND" . |
||
5486 | " mchildren.l_type='FAMS' AND" . |
||
5487 | " mchildren.l_to=gchildren.l_from AND" . |
||
5488 | " gchildren.l_type='CHIL'" . |
||
5489 | " GROUP BY id" . |
||
5490 | " ORDER BY tot DESC" . |
||
5491 | " LIMIT " . $total |
||
5492 | ); |
||
5493 | if (!isset($rows[0])) { |
||
5494 | return ''; |
||
5495 | } |
||
5496 | $top10 = array(); |
||
5497 | foreach ($rows as $row) { |
||
5498 | $family = Family::getInstance($row['id'], $this->tree); |
||
5499 | if ($family->canShow()) { |
||
5500 | if ($type === 'list') { |
||
5501 | $top10[] = |
||
5502 | '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' . |
||
5503 | I18N::plural('%s grandchild', '%s grandchildren', $row['tot'], I18N::number($row['tot'])); |
||
5504 | } else { |
||
5505 | $top10[] = |
||
5506 | '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' . |
||
5507 | I18N::plural('%s grandchild', '%s grandchildren', $row['tot'], I18N::number($row['tot'])); |
||
5508 | } |
||
5509 | } |
||
5510 | } |
||
5511 | if ($type === 'list') { |
||
5512 | $top10 = implode('', $top10); |
||
5513 | } else { |
||
5514 | $top10 = implode('; ', $top10); |
||
5515 | } |
||
5516 | if (I18N::direction() === 'rtl') { |
||
5517 | $top10 = str_replace(array('[', ']', '(', ')', '+'), array('‏[', '‏]', '‏(', '‏)', '‏+'), $top10); |
||
5518 | } |
||
5519 | if ($type === 'list') { |
||
5520 | return '<ul>' . $top10 . '</ul>'; |
||
5521 | } |
||
5522 | |||
5523 | return $top10; |
||
5524 | } |
||
5525 | |||
5526 | /** |
||
5527 | * Find the couple with the most grandchildren. |
||
5528 | * |
||
5529 | * @param string[] $params |
||
5530 | * |
||
5531 | * @return string |
||
5532 | */ |
||
5533 | public function topTenLargestGrandFamily($params = array()) |
||
5534 | { |
||
5535 | return $this->topTenGrandFamilyQuery('nolist', $params); |
||
5536 | } |
||
5537 | |||
5538 | /** |
||
5539 | * Find the couple with the most grandchildren. |
||
5540 | * |
||
5541 | * @param string[] $params |
||
5542 | * |
||
5543 | * @return string |
||
5544 | */ |
||
5545 | public function topTenLargestGrandFamilyList($params = array()) |
||
5546 | { |
||
5547 | return $this->topTenGrandFamilyQuery('list', $params); |
||
5548 | } |
||
5549 | |||
5550 | /** |
||
5551 | * Find common surnames. |
||
5552 | * |
||
5553 | * @param string $type |
||
5554 | * @param bool $show_tot |
||
5555 | * @param string[] $params |
||
5556 | * |
||
5557 | * @return string |
||
5558 | */ |
||
5559 | private function commonSurnamesQuery($type = 'list', $show_tot = false, $params = array()) |
||
5560 | { |
||
5561 | $threshold = empty($params[0]) ? 10 : (int) $params[0]; |
||
5562 | $number_of_surnames = empty($params[1]) ? 10 : (int) $params[1]; |
||
5563 | $sorting = empty($params[2]) ? 'alpha' : $params[2]; |
||
5564 | |||
5565 | $surname_list = FunctionsDb::getTopSurnames($this->tree->getTreeId(), $threshold, $number_of_surnames); |
||
5566 | if (empty($surname_list)) { |
||
5567 | return ''; |
||
5568 | } |
||
5569 | |||
5570 | switch ($sorting) { |
||
5571 | default: |
||
5572 | case 'alpha': |
||
5573 | uksort($surname_list, '\Fisharebest\Webtrees\I18N::strcasecmp'); |
||
5574 | break; |
||
5575 | case 'count': |
||
5576 | asort($surname_list); |
||
5577 | break; |
||
5578 | case 'rcount': |
||
5579 | arsort($surname_list); |
||
5580 | break; |
||
5581 | } |
||
5582 | |||
5583 | // Note that we count/display SPFX SURN, but sort/group under just SURN |
||
5584 | $surnames = array(); |
||
5585 | foreach (array_keys($surname_list) as $surname) { |
||
5586 | $surnames = array_merge($surnames, QueryName::surnames($this->tree, $surname, '', false, false)); |
||
5587 | } |
||
5588 | |||
5589 | return FunctionsPrintLists::surnameList($surnames, ($type == 'list' ? 1 : 2), $show_tot, 'indilist.php', $this->tree); |
||
5590 | } |
||
5591 | |||
5592 | /** |
||
5593 | * Find common surnames. |
||
5594 | * |
||
5595 | * @return string |
||
5596 | */ |
||
5597 | public function getCommonSurname() |
||
5598 | { |
||
5599 | $surnames = array_keys(FunctionsDb::getTopSurnames($this->tree->getTreeId(), 1, 1)); |
||
5600 | |||
5601 | return array_shift($surnames); |
||
5602 | } |
||
5603 | |||
5604 | /** |
||
5605 | * Find common surnames. |
||
5606 | * |
||
5607 | * @param string[] $params |
||
5608 | * |
||
5609 | * @return string |
||
5610 | */ |
||
5611 | public function commonSurnames($params = array('', '', 'alpha')) |
||
5612 | { |
||
5613 | return $this->commonSurnamesQuery('nolist', false, $params); |
||
5614 | } |
||
5615 | |||
5616 | /** |
||
5617 | * Find common surnames. |
||
5618 | * |
||
5619 | * @param string[] $params |
||
5620 | * |
||
5621 | * @return string |
||
5622 | */ |
||
5623 | public function commonSurnamesTotals($params = array('', '', 'rcount')) |
||
5624 | { |
||
5625 | return $this->commonSurnamesQuery('nolist', true, $params); |
||
5626 | } |
||
5627 | |||
5628 | /** |
||
5629 | * Find common surnames. |
||
5630 | * |
||
5631 | * @param string[] $params |
||
5632 | * |
||
5633 | * @return string |
||
5634 | */ |
||
5635 | public function commonSurnamesList($params = array('', '', 'alpha')) |
||
5636 | { |
||
5637 | return $this->commonSurnamesQuery('list', false, $params); |
||
5638 | } |
||
5639 | |||
5640 | /** |
||
5641 | * Find common surnames. |
||
5642 | * |
||
5643 | * @param string[] $params |
||
5644 | * |
||
5645 | * @return string |
||
5646 | */ |
||
5647 | public function commonSurnamesListTotals($params = array('', '', 'rcount')) |
||
5648 | { |
||
5649 | return $this->commonSurnamesQuery('list', true, $params); |
||
5650 | } |
||
5651 | |||
5652 | /** |
||
5653 | * Create a chart of common surnames. |
||
5654 | * |
||
5655 | * @param string[] $params |
||
5656 | * |
||
5657 | * @return string |
||
5658 | */ |
||
5659 | public function chartCommonSurnames($params = array()) |
||
5660 | { |
||
5661 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
5662 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
5663 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
5664 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
5665 | |||
5666 | $size = empty($params[0]) ? $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y : strtolower($params[0]); |
||
5667 | $color_from = empty($params[1]) ? $WT_STATS_CHART_COLOR1 : strtolower($params[1]); |
||
5668 | $color_to = empty($params[2]) ? $WT_STATS_CHART_COLOR2 : strtolower($params[2]); |
||
5669 | $number_of_surnames = empty($params[3]) ? 10 : (int) $params[3]; |
||
5670 | |||
5671 | $sizes = explode('x', $size); |
||
5672 | $tot_indi = $this->totalIndividualsQuery(); |
||
5673 | $surnames = FunctionsDb::getTopSurnames($this->tree->getTreeId(), 0, $number_of_surnames); |
||
5674 | if (empty($surnames)) { |
||
5675 | return ''; |
||
5676 | } |
||
5677 | $SURNAME_TRADITION = $this->tree->getPreference('SURNAME_TRADITION'); |
||
5678 | $all_surnames = array(); |
||
5679 | $tot = 0; |
||
5680 | foreach ($surnames as $surname => $num) { |
||
5681 | $all_surnames = array_merge($all_surnames, QueryName::surnames($this->tree, I18N::strtoupper($surname), '', false, false)); |
||
5682 | $tot += $num; |
||
5683 | } |
||
5684 | $chd = ''; |
||
5685 | $chl = array(); |
||
5686 | foreach ($all_surnames as $surns) { |
||
5687 | $count_per = 0; |
||
5688 | $max_name = 0; |
||
5689 | $top_name = ''; |
||
5690 | foreach ($surns as $spfxsurn => $indis) { |
||
5691 | $per = count($indis); |
||
5692 | $count_per += $per; |
||
5693 | // select most common surname from all variants |
||
5694 | if ($per > $max_name) { |
||
5695 | $max_name = $per; |
||
5696 | $top_name = $spfxsurn; |
||
5697 | } |
||
5698 | } |
||
5699 | switch ($SURNAME_TRADITION) { |
||
5700 | case 'polish': |
||
5701 | // most common surname should be in male variant (Kowalski, not Kowalska) |
||
5702 | $top_name = preg_replace(array('/ska$/', '/cka$/', '/dzka$/', '/żka$/'), array('ski', 'cki', 'dzki', 'żki'), $top_name); |
||
5703 | } |
||
5704 | $per = round(100 * $count_per / $tot_indi, 0); |
||
5705 | $chd .= $this->arrayToExtendedEncoding(array($per)); |
||
5706 | $chl[] = $top_name . ' - ' . I18N::number($count_per); |
||
5707 | |||
5708 | } |
||
5709 | $per = round(100 * ($tot_indi - $tot) / $tot_indi, 0); |
||
5710 | $chd .= $this->arrayToExtendedEncoding(array($per)); |
||
5711 | $chl[] = I18N::translate('Other') . ' - ' . I18N::number($tot_indi - $tot); |
||
5712 | |||
5713 | $chart_title = implode(I18N::$list_separator, $chl); |
||
5714 | $chl = implode('|', $chl); |
||
5715 | |||
5716 | return '<img src="https://chart.googleapis.com/chart?cht=p3&chd=e:' . $chd . '&chs=' . $size . '&chco=' . $color_from . ',' . $color_to . '&chf=bg,s,ffffff00&chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '" />'; |
||
5717 | } |
||
5718 | |||
5719 | /** |
||
5720 | * Find common given names. |
||
5721 | * |
||
5722 | * @param string $sex |
||
5723 | * @param string $type |
||
5724 | * @param bool $show_tot |
||
5725 | * @param string[] $params |
||
5726 | * |
||
5727 | * @return string |
||
5728 | */ |
||
5729 | private function commonGivenQuery($sex = 'B', $type = 'list', $show_tot = false, $params = array()) |
||
5730 | { |
||
5731 | if (isset($params[0]) && $params[0] != '' && $params[0] >= 0) { |
||
5732 | $threshold = (int) $params[0]; |
||
5733 | } else { |
||
5734 | $threshold = 1; |
||
5735 | } |
||
5736 | if (isset($params[1]) && $params[1] != '' && $params[1] >= 0) { |
||
5737 | $maxtoshow = (int) $params[1]; |
||
5738 | } else { |
||
5739 | $maxtoshow = 10; |
||
5740 | } |
||
5741 | |||
5742 | switch ($sex) { |
||
5743 | case 'M': |
||
5744 | $sex_sql = "i_sex='M'"; |
||
5745 | break; |
||
5746 | case 'F': |
||
5747 | $sex_sql = "i_sex='F'"; |
||
5748 | break; |
||
5749 | case 'U': |
||
5750 | $sex_sql = "i_sex='U'"; |
||
5751 | break; |
||
5752 | case 'B': |
||
5753 | default: |
||
5754 | $sex_sql = "i_sex<>'U'"; |
||
5755 | break; |
||
5756 | } |
||
5757 | $ged_id = $this->tree->getTreeId(); |
||
5758 | |||
5759 | $rows = Database::prepare("SELECT n_givn, COUNT(*) AS num FROM `##name` JOIN `##individuals` ON (n_id=i_id AND n_file=i_file) WHERE n_file={$ged_id} AND n_type<>'_MARNM' AND n_givn NOT IN ('@P.N.', '') AND LENGTH(n_givn)>1 AND {$sex_sql} GROUP BY n_id, n_givn") |
||
5760 | ->fetchAll(); |
||
5761 | $nameList = array(); |
||
5762 | foreach ($rows as $row) { |
||
5763 | // Split “John Thomas” into “John” and “Thomas” and count against both totals |
||
5764 | foreach (explode(' ', $row->n_givn) as $given) { |
||
5765 | // Exclude initials and particles. |
||
5766 | if (!preg_match('/^([A-Z]|[a-z]{1,3})$/', $given)) { |
||
5767 | if (array_key_exists($given, $nameList)) { |
||
5768 | $nameList[$given] += $row->num; |
||
5769 | } else { |
||
5770 | $nameList[$given] = $row->num; |
||
5771 | } |
||
5772 | } |
||
5773 | } |
||
5774 | } |
||
5775 | arsort($nameList, SORT_NUMERIC); |
||
5776 | $nameList = array_slice($nameList, 0, $maxtoshow); |
||
5777 | |||
5778 | if (count($nameList) == 0) { |
||
5779 | return ''; |
||
5780 | } |
||
5781 | if ($type == 'chart') { |
||
5782 | return $nameList; |
||
5783 | } |
||
5784 | $common = array(); |
||
5785 | foreach ($nameList as $given => $total) { |
||
5786 | if ($maxtoshow !== -1) { |
||
5787 | if ($maxtoshow-- <= 0) { |
||
5788 | break; |
||
5789 | } |
||
5790 | } |
||
5791 | if ($total < $threshold) { |
||
5792 | break; |
||
5793 | } |
||
5794 | if ($show_tot) { |
||
5795 | $tot = ' (' . I18N::number($total) . ')'; |
||
5796 | } else { |
||
5797 | $tot = ''; |
||
5798 | } |
||
5799 | switch ($type) { |
||
5800 | case 'table': |
||
5801 | $common[] = '<tr><td>' . $given . '</td><td>' . I18N::number($total) . '</td><td>' . $total . '</td></tr>'; |
||
5802 | break; |
||
5803 | case 'list': |
||
5804 | $common[] = '<li><span dir="auto">' . $given . '</span>' . $tot . '</li>'; |
||
5805 | break; |
||
5806 | case 'nolist': |
||
5807 | $common[] = '<span dir="auto">' . $given . '</span>' . $tot; |
||
5808 | break; |
||
5809 | } |
||
5810 | } |
||
5811 | if ($common) { |
||
5812 | switch ($type) { |
||
5813 | case 'table': |
||
5814 | global $controller; |
||
5815 | $table_id = Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page |
||
5816 | $controller |
||
5817 | ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL) |
||
5818 | ->addInlineJavascript(' |
||
5819 | jQuery("#' . $table_id . '").dataTable({ |
||
5820 | dom: \'t\', |
||
5821 | autoWidth: false, |
||
5822 | paging: false, |
||
5823 | lengthChange: false, |
||
5824 | filter: false, |
||
5825 | info: false, |
||
5826 | jQueryUI: true, |
||
5827 | sorting: [[1,"desc"]], |
||
5828 | columns: [ |
||
5829 | /* 0-name */ {}, |
||
5830 | /* 1-count */ { class: "center", dataSort: 2}, |
||
5831 | /* 2-COUNT */ { visible: false} |
||
5832 | ] |
||
5833 | }); |
||
5834 | jQuery("#' . $table_id . '").css("visibility", "visible"); |
||
5835 | '); |
||
5836 | $lookup = array('M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown'), 'B' => I18N::translate('All')); |
||
5837 | |||
5838 | return '<table id="' . $table_id . '" class="givn-list"><thead><tr><th class="ui-state-default" colspan="3">' . $lookup[$sex] . '</th></tr><tr><th>' . I18N::translate('Name') . '</th><th>' . I18N::translate('Count') . '</th><th>COUNT</th></tr></thead><tbody>' . implode('', $common) . '</tbody></table>'; |
||
5839 | case 'list': |
||
5840 | return '<ul>' . implode('', $common) . '</ul>'; |
||
5841 | case 'nolist': |
||
5842 | return implode(I18N::$list_separator, $common); |
||
5843 | default: |
||
5844 | return ''; |
||
5845 | } |
||
5846 | } else { |
||
5847 | return ''; |
||
5848 | } |
||
5849 | } |
||
5850 | |||
5851 | /** |
||
5852 | * Find common give names. |
||
5853 | * |
||
5854 | * @param string[] $params |
||
5855 | * |
||
5856 | * @return string |
||
5857 | */ |
||
5858 | public function commonGiven($params = array(1, 10, 'alpha')) |
||
5859 | { |
||
5860 | return $this->commonGivenQuery('B', 'nolist', false, $params); |
||
5861 | } |
||
5862 | |||
5863 | /** |
||
5864 | * Find common give names. |
||
5865 | * |
||
5866 | * @param string[] $params |
||
5867 | * |
||
5868 | * @return string |
||
5869 | */ |
||
5870 | public function commonGivenTotals($params = array(1, 10, 'rcount')) |
||
5871 | { |
||
5872 | return $this->commonGivenQuery('B', 'nolist', true, $params); |
||
5873 | } |
||
5874 | |||
5875 | /** |
||
5876 | * Find common give names. |
||
5877 | * |
||
5878 | * @param string[] $params |
||
5879 | * |
||
5880 | * @return string |
||
5881 | */ |
||
5882 | public function commonGivenList($params = array(1, 10, 'alpha')) |
||
5883 | { |
||
5884 | return $this->commonGivenQuery('B', 'list', false, $params); |
||
5885 | } |
||
5886 | |||
5887 | /** |
||
5888 | * Find common give names. |
||
5889 | * |
||
5890 | * @param string[] $params |
||
5891 | * |
||
5892 | * @return string |
||
5893 | */ |
||
5894 | public function commonGivenListTotals($params = array(1, 10, 'rcount')) |
||
5895 | { |
||
5896 | return $this->commonGivenQuery('B', 'list', true, $params); |
||
5897 | } |
||
5898 | |||
5899 | /** |
||
5900 | * Find common give names. |
||
5901 | * |
||
5902 | * @param string[] $params |
||
5903 | * |
||
5904 | * @return string |
||
5905 | */ |
||
5906 | public function commonGivenTable($params = array(1, 10, 'rcount')) |
||
5907 | { |
||
5908 | return $this->commonGivenQuery('B', 'table', false, $params); |
||
5909 | } |
||
5910 | |||
5911 | /** |
||
5912 | * Find common give names of females. |
||
5913 | * |
||
5914 | * @param string[] $params |
||
5915 | * |
||
5916 | * @return string |
||
5917 | */ |
||
5918 | public function commonGivenFemale($params = array(1, 10, 'alpha')) |
||
5919 | { |
||
5920 | return $this->commonGivenQuery('F', 'nolist', false, $params); |
||
5921 | } |
||
5922 | |||
5923 | /** |
||
5924 | * Find common give names of females. |
||
5925 | * |
||
5926 | * @param string[] $params |
||
5927 | * |
||
5928 | * @return string |
||
5929 | */ |
||
5930 | public function commonGivenFemaleTotals($params = array(1, 10, 'rcount')) |
||
5931 | { |
||
5932 | return $this->commonGivenQuery('F', 'nolist', true, $params); |
||
5933 | } |
||
5934 | |||
5935 | /** |
||
5936 | * Find common give names of females. |
||
5937 | * |
||
5938 | * @param string[] $params |
||
5939 | * |
||
5940 | * @return string |
||
5941 | */ |
||
5942 | public function commonGivenFemaleList($params = array(1, 10, 'alpha')) |
||
5943 | { |
||
5944 | return $this->commonGivenQuery('F', 'list', false, $params); |
||
5945 | } |
||
5946 | |||
5947 | /** |
||
5948 | * Find common give names of females. |
||
5949 | * |
||
5950 | * @param string[] $params |
||
5951 | * |
||
5952 | * @return string |
||
5953 | */ |
||
5954 | public function commonGivenFemaleListTotals($params = array(1, 10, 'rcount')) |
||
5955 | { |
||
5956 | return $this->commonGivenQuery('F', 'list', true, $params); |
||
5957 | } |
||
5958 | |||
5959 | /** |
||
5960 | * Find common give names of females. |
||
5961 | * |
||
5962 | * @param string[] $params |
||
5963 | * |
||
5964 | * @return string |
||
5965 | */ |
||
5966 | public function commonGivenFemaleTable($params = array(1, 10, 'rcount')) |
||
5967 | { |
||
5968 | return $this->commonGivenQuery('F', 'table', false, $params); |
||
5969 | } |
||
5970 | |||
5971 | /** |
||
5972 | * Find common give names of males. |
||
5973 | * |
||
5974 | * @param string[] $params |
||
5975 | * |
||
5976 | * @return string |
||
5977 | */ |
||
5978 | public function commonGivenMale($params = array(1, 10, 'alpha')) |
||
5979 | { |
||
5980 | return $this->commonGivenQuery('M', 'nolist', false, $params); |
||
5981 | } |
||
5982 | |||
5983 | /** |
||
5984 | * Find common give names of males. |
||
5985 | * |
||
5986 | * @param string[] $params |
||
5987 | * |
||
5988 | * @return string |
||
5989 | */ |
||
5990 | public function commonGivenMaleTotals($params = array(1, 10, 'rcount')) |
||
5991 | { |
||
5992 | return $this->commonGivenQuery('M', 'nolist', true, $params); |
||
5993 | } |
||
5994 | |||
5995 | /** |
||
5996 | * Find common give names of males. |
||
5997 | * |
||
5998 | * @param string[] $params |
||
5999 | * |
||
6000 | * @return string |
||
6001 | */ |
||
6002 | public function commonGivenMaleList($params = array(1, 10, 'alpha')) |
||
6003 | { |
||
6004 | return $this->commonGivenQuery('M', 'list', false, $params); |
||
6005 | } |
||
6006 | |||
6007 | /** |
||
6008 | * Find common give names of males. |
||
6009 | * |
||
6010 | * @param string[] $params |
||
6011 | * |
||
6012 | * @return string |
||
6013 | */ |
||
6014 | public function commonGivenMaleListTotals($params = array(1, 10, 'rcount')) |
||
6015 | { |
||
6016 | return $this->commonGivenQuery('M', 'list', true, $params); |
||
6017 | } |
||
6018 | |||
6019 | /** |
||
6020 | * Find common give names of males. |
||
6021 | * |
||
6022 | * @param string[] $params |
||
6023 | * |
||
6024 | * @return string |
||
6025 | */ |
||
6026 | public function commonGivenMaleTable($params = array(1, 10, 'rcount')) |
||
6027 | { |
||
6028 | return $this->commonGivenQuery('M', 'table', false, $params); |
||
6029 | } |
||
6030 | |||
6031 | /** |
||
6032 | * Find common give names of unknown sexes. |
||
6033 | * |
||
6034 | * @param string[] $params |
||
6035 | * |
||
6036 | * @return string |
||
6037 | */ |
||
6038 | public function commonGivenUnknown($params = array(1, 10, 'alpha')) |
||
6039 | { |
||
6040 | return $this->commonGivenQuery('U', 'nolist', false, $params); |
||
6041 | } |
||
6042 | |||
6043 | /** |
||
6044 | * Find common give names of unknown sexes. |
||
6045 | * |
||
6046 | * @param string[] $params |
||
6047 | * |
||
6048 | * @return string |
||
6049 | */ |
||
6050 | public function commonGivenUnknownTotals($params = array(1, 10, 'rcount')) |
||
6051 | { |
||
6052 | return $this->commonGivenQuery('U', 'nolist', true, $params); |
||
6053 | } |
||
6054 | |||
6055 | /** |
||
6056 | * Find common give names of unknown sexes. |
||
6057 | * |
||
6058 | * @param string[] $params |
||
6059 | * |
||
6060 | * @return string |
||
6061 | */ |
||
6062 | public function commonGivenUnknownList($params = array(1, 10, 'alpha')) |
||
6063 | { |
||
6064 | return $this->commonGivenQuery('U', 'list', false, $params); |
||
6065 | } |
||
6066 | |||
6067 | /** |
||
6068 | * Find common give names of unknown sexes. |
||
6069 | * |
||
6070 | * @param string[] $params |
||
6071 | * |
||
6072 | * @return string |
||
6073 | */ |
||
6074 | public function commonGivenUnknownListTotals($params = array(1, 10, 'rcount')) |
||
6075 | { |
||
6076 | return $this->commonGivenQuery('U', 'list', true, $params); |
||
6077 | } |
||
6078 | |||
6079 | /** |
||
6080 | * Find common give names of unknown sexes. |
||
6081 | * |
||
6082 | * @param string[] $params |
||
6083 | * |
||
6084 | * @return string |
||
6085 | */ |
||
6086 | public function commonGivenUnknownTable($params = array(1, 10, 'rcount')) |
||
6087 | { |
||
6088 | return $this->commonGivenQuery('U', 'table', false, $params); |
||
6089 | } |
||
6090 | |||
6091 | /** |
||
6092 | * Create a chart of common given names. |
||
6093 | * |
||
6094 | * @param string[] $params |
||
6095 | * |
||
6096 | * @return string |
||
6097 | */ |
||
6098 | public function chartCommonGiven($params = array()) |
||
6099 | { |
||
6100 | $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values'); |
||
6101 | $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values'); |
||
6102 | $WT_STATS_S_CHART_X = Theme::theme()->parameter('stats-small-chart-x'); |
||
6103 | $WT_STATS_S_CHART_Y = Theme::theme()->parameter('stats-small-chart-y'); |
||
6104 | |||
6105 | if (isset($params[0]) && $params[0] != '') { |
||
6106 | $size = strtolower($params[0]); |
||
6107 | } else { |
||
6108 | $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; |
||
6109 | } |
||
6110 | if (isset($params[1]) && $params[1] != '') { |
||
6111 | $color_from = strtolower($params[1]); |
||
6112 | } else { |
||
6113 | $color_from = $WT_STATS_CHART_COLOR1; |
||
6114 | } |
||
6115 | if (isset($params[2]) && $params[2] != '') { |
||
6116 | $color_to = strtolower($params[2]); |
||
6117 | } else { |
||
6118 | $color_to = $WT_STATS_CHART_COLOR2; |
||
6119 | } |
||
6120 | if (isset($params[4]) && $params[4] != '') { |
||
6121 | $maxtoshow = strtolower($params[4]); |
||
6122 | } else { |
||
6123 | $maxtoshow = 7; |
||
6124 | } |
||
6125 | $sizes = explode('x', $size); |
||
6126 | $tot_indi = $this->totalIndividualsQuery(); |
||
6127 | $given = $this->commonGivenQuery('B', 'chart'); |
||
6128 | if (!is_array($given)) { |
||
6129 | return ''; |
||
6130 | } |
||
6131 | $given = array_slice($given, 0, $maxtoshow); |
||
6132 | if (count($given) <= 0) { |
||
6133 | return ''; |
||
6134 | } |
||
6135 | $tot = 0; |
||
6136 | foreach ($given as $count) { |
||
6137 | $tot += $count; |
||
6138 | } |
||
6139 | $chd = ''; |
||
6140 | $chl = array(); |
||
6141 | foreach ($given as $givn => $count) { |
||
6142 | if ($tot == 0) { |
||
6143 | $per = 0; |
||
6144 | } else { |
||
6145 | $per = round(100 * $count / $tot_indi, 0); |
||
6146 | } |
||
6147 | $chd .= $this->arrayToExtendedEncoding(array($per)); |
||
6148 | $chl[] = $givn . ' - ' . I18N::number($count); |
||
6149 | } |
||
6150 | $per = round(100 * ($tot_indi - $tot) / $tot_indi, 0); |
||
6151 | $chd .= $this->arrayToExtendedEncoding(array($per)); |
||
6152 | $chl[] = I18N::translate('Other') . ' - ' . I18N::number($tot_indi - $tot); |
||
6153 | |||
6154 | $chart_title = implode(I18N::$list_separator, $chl); |
||
6155 | $chl = implode('|', $chl); |
||
6156 | |||
6157 | return "<img src=\"https://chart.googleapis.com/chart?cht=p3&chd=e:{$chd}&chs={$size}&chco={$color_from},{$color_to}&chf=bg,s,ffffff00&chl=" . rawurlencode($chl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; |
||
6158 | } |
||
6159 | |||
6160 | /** |
||
6161 | * Who is currently logged in? |
||
6162 | * |
||
6163 | * @param string $type |
||
6164 | * |
||
6165 | * @return string |
||
6166 | */ |
||
6167 | private function usersLoggedInQuery($type = 'nolist') |
||
6168 | { |
||
6169 | $content = ''; |
||
6170 | // List active users |
||
6171 | $NumAnonymous = 0; |
||
6172 | $loggedusers = array(); |
||
6173 | foreach (User::allLoggedIn() as $user) { |
||
6174 | if (Auth::isAdmin() || $user->getPreference('visibleonline')) { |
||
6175 | $loggedusers[] = $user; |
||
6176 | } else { |
||
6177 | $NumAnonymous++; |
||
6178 | } |
||
6179 | } |
||
6180 | $LoginUsers = count($loggedusers); |
||
6181 | if ($LoginUsers == 0 && $NumAnonymous == 0) { |
||
6182 | return I18N::translate('No signed-in and no anonymous users'); |
||
6183 | } |
||
6184 | if ($NumAnonymous > 0) { |
||
6185 | $content .= '<b>' . I18N::plural('%s anonymous signed-in user', '%s anonymous signed-in users', $NumAnonymous, I18N::number($NumAnonymous)) . '</b>'; |
||
6186 | } |
||
6187 | if ($LoginUsers > 0) { |
||
6188 | if ($NumAnonymous) { |
||
6189 | if ($type == 'list') { |
||
6190 | $content .= "<br><br>"; |
||
6191 | } else { |
||
6192 | $content .= " " . I18N::translate('and') . " "; |
||
6193 | } |
||
6194 | } |
||
6195 | $content .= '<b>' . I18N::plural('%s signed-in user', '%s signed-in users', $LoginUsers, I18N::number($LoginUsers)) . '</b>'; |
||
6196 | if ($type == 'list') { |
||
6197 | $content .= '<ul>'; |
||
6198 | } else { |
||
6199 | $content .= ': '; |
||
6200 | } |
||
6201 | } |
||
6202 | if (Auth::check()) { |
||
6203 | foreach ($loggedusers as $user) { |
||
6204 | if ($type == 'list') { |
||
6205 | $content .= '<li>' . Filter::escapeHtml($user->getRealName()) . ' - ' . Filter::escapeHtml($user->getUserName()); |
||
6206 | } else { |
||
6207 | $content .= Filter::escapeHtml($user->getRealName()) . ' - ' . Filter::escapeHtml($user->getUserName()); |
||
6208 | } |
||
6209 | if (Auth::id() != $user->getUserId() && $user->getPreference('contactmethod') != 'none') { |
||
6210 | if ($type == 'list') { |
||
6211 | $content .= '<br><a class="icon-email" href="#" onclick="return message(\'' . $user->getUserId() . '\', \'\', \'' . Filter::escapeJs(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>'; |
||
6212 | } else { |
||
6213 | $content .= ' <a class="icon-email" href="#" onclick="return message(\'' . $user->getUserId() . '\', \'\', \'' . Filter::escapeJs(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>'; |
||
6214 | } |
||
6215 | } |
||
6216 | if ($type == 'list') { |
||
6217 | $content .= '</li>'; |
||
6218 | } |
||
6219 | } |
||
6220 | } |
||
6221 | if ($type == 'list') { |
||
6222 | $content .= '</ul>'; |
||
6223 | } |
||
6224 | |||
6225 | return $content; |
||
6226 | } |
||
6227 | |||
6228 | /** |
||
6229 | * NUmber of users who are currently logged in? |
||
6230 | * |
||
6231 | * @param string $type |
||
6232 | * |
||
6233 | * @return int |
||
6234 | */ |
||
6235 | private function usersLoggedInTotalQuery($type = 'all') |
||
6236 | { |
||
6237 | $anon = 0; |
||
6238 | $visible = 0; |
||
6239 | foreach (User::allLoggedIn() as $user) { |
||
6240 | if (Auth::isAdmin() || $user->getPreference('visibleonline')) { |
||
6241 | $visible++; |
||
6242 | } else { |
||
6243 | $anon++; |
||
6244 | } |
||
6245 | } |
||
6246 | if ($type == 'anon') { |
||
6247 | return $anon; |
||
6248 | } elseif ($type == 'visible') { |
||
6249 | return $visible; |
||
6250 | } else { |
||
6251 | return $visible + $anon; |
||
6252 | } |
||
6253 | } |
||
6254 | |||
6255 | /** |
||
6256 | * Who is currently logged in? |
||
6257 | * |
||
6258 | * @return string |
||
6259 | */ |
||
6260 | public function usersLoggedIn() |
||
6261 | { |
||
6262 | return $this->usersLoggedInQuery('nolist'); |
||
6263 | } |
||
6264 | |||
6265 | /** |
||
6266 | * Who is currently logged in? |
||
6267 | * |
||
6268 | * @return string |
||
6269 | */ |
||
6270 | public function usersLoggedInList() |
||
6271 | { |
||
6272 | return $this->usersLoggedInQuery('list'); |
||
6273 | } |
||
6274 | |||
6275 | /** |
||
6276 | * Who is currently logged in? |
||
6277 | * |
||
6278 | * @return int |
||
6279 | */ |
||
6280 | public function usersLoggedInTotal() |
||
6281 | { |
||
6282 | return $this->usersLoggedInTotalQuery('all'); |
||
6283 | } |
||
6284 | |||
6285 | /** |
||
6286 | * Which visitors are currently logged in? |
||
6287 | * |
||
6288 | * @return int |
||
6289 | */ |
||
6290 | public function usersLoggedInTotalAnon() |
||
6291 | { |
||
6292 | return $this->usersLoggedInTotalQuery('anon'); |
||
6293 | } |
||
6294 | |||
6295 | /** |
||
6296 | * Which visitors are currently logged in? |
||
6297 | * |
||
6298 | * @return int |
||
6299 | */ |
||
6300 | public function usersLoggedInTotalVisible() |
||
6301 | { |
||
6302 | return $this->usersLoggedInTotalQuery('visible'); |
||
6303 | } |
||
6304 | |||
6305 | /** |
||
6306 | * Get the current user's ID. |
||
6307 | * |
||
6308 | * @return null|string |
||
6309 | */ |
||
6310 | public function userId() |
||
6311 | { |
||
6312 | return Auth::id(); |
||
6313 | } |
||
6314 | |||
6315 | /** |
||
6316 | * Get the current user's username. |
||
6317 | * |
||
6318 | * @param string[] $params |
||
6319 | * |
||
6320 | * @return string |
||
6321 | */ |
||
6322 | public function userName($params = array()) |
||
6323 | { |
||
6324 | if (Auth::check()) { |
||
6325 | return Filter::escapeHtml(Auth::user()->getUserName()); |
||
6326 | } elseif (isset($params[0]) && $params[0] != '') { |
||
6327 | // if #username:visitor# was specified, then "visitor" will be returned when the user is not logged in |
||
6328 | return Filter::escapeHtml($params[0]); |
||
6329 | } else { |
||
6330 | return ''; |
||
6331 | } |
||
6332 | } |
||
6333 | |||
6334 | /** |
||
6335 | * Get the current user's full name. |
||
6336 | * |
||
6337 | * @return string |
||
6338 | */ |
||
6339 | public function userFullName() |
||
6340 | { |
||
6341 | return Auth::check() ? Auth::user()->getRealNameHtml() : ''; |
||
6342 | } |
||
6343 | |||
6344 | /** |
||
6345 | * Get the newest registered user. |
||
6346 | * |
||
6347 | * @param string $type |
||
6348 | * @param string[] $params |
||
6349 | * |
||
6350 | * @return string |
||
6351 | */ |
||
6352 | private function getLatestUserData($type = 'userid', $params = array()) |
||
6353 | { |
||
6354 | static $user_id = null; |
||
6355 | |||
6356 | if ($user_id === null) { |
||
6357 | $user = User::findLatestToRegister(); |
||
6358 | } else { |
||
6359 | $user = User::find($user_id); |
||
6360 | } |
||
6361 | |||
6362 | switch ($type) { |
||
6363 | default: |
||
6364 | case 'userid': |
||
6365 | return $user->getUserId(); |
||
6366 | case 'username': |
||
6367 | return Filter::escapeHtml($user->getUserName()); |
||
6368 | case 'fullname': |
||
6369 | return $user->getRealNameHtml(); |
||
6370 | case 'regdate': |
||
6371 | if (is_array($params) && isset($params[0]) && $params[0] != '') { |
||
6372 | $datestamp = $params[0]; |
||
6373 | } else { |
||
6374 | $datestamp = I18N::dateFormat(); |
||
6375 | } |
||
6376 | |||
6377 | return FunctionsDate::timestampToGedcomDate($user->getPreference('reg_timestamp'))->display(false, $datestamp); |
||
6378 | case 'regtime': |
||
6379 | if (is_array($params) && isset($params[0]) && $params[0] != '') { |
||
6380 | $datestamp = $params[0]; |
||
6381 | } else { |
||
6382 | $datestamp = str_replace('%', '', I18N::timeFormat()); |
||
6383 | } |
||
6384 | |||
6385 | return date($datestamp, $user->getPreference('reg_timestamp')); |
||
6386 | case 'loggedin': |
||
6387 | if (is_array($params) && isset($params[0]) && $params[0] != '') { |
||
6388 | $yes = $params[0]; |
||
6389 | } else { |
||
6390 | $yes = I18N::translate('yes'); |
||
6391 | } |
||
6392 | if (is_array($params) && isset($params[1]) && $params[1] != '') { |
||
6393 | $no = $params[1]; |
||
6394 | } else { |
||
6395 | $no = I18N::translate('no'); |
||
6396 | } |
||
6397 | |||
6398 | return Database::prepare("SELECT 1 FROM `##session` WHERE user_id=? LIMIT 1")->execute(array($user->getUserId()))->fetchOne() ? $yes : $no; |
||
6399 | } |
||
6400 | } |
||
6401 | |||
6402 | /** |
||
6403 | * Get the newest registered user's ID. |
||
6404 | * |
||
6405 | * @return string |
||
6406 | */ |
||
6407 | public function latestUserId() |
||
6408 | { |
||
6409 | return $this->getLatestUserData('userid'); |
||
6410 | } |
||
6411 | |||
6412 | /** |
||
6413 | * Get the newest registered user's username. |
||
6414 | * |
||
6415 | * @return string |
||
6416 | */ |
||
6417 | public function latestUserName() |
||
6418 | { |
||
6419 | return $this->getLatestUserData('username'); |
||
6420 | } |
||
6421 | |||
6422 | /** |
||
6423 | * Get the newest registered user's real name. |
||
6424 | * |
||
6425 | * @return string |
||
6426 | */ |
||
6427 | public function latestUserFullName() |
||
6428 | { |
||
6429 | return $this->getLatestUserData('fullname'); |
||
6430 | } |
||
6431 | |||
6432 | /** |
||
6433 | * Get the date of the newest user registration. |
||
6434 | * |
||
6435 | * @param string[] $params |
||
6436 | * |
||
6437 | * @return string |
||
6438 | */ |
||
6439 | public function latestUserRegDate($params = array()) |
||
6440 | { |
||
6441 | return $this->getLatestUserData('regdate', $params); |
||
6442 | } |
||
6443 | |||
6444 | /** |
||
6445 | * Find the timestamp of the latest user to register. |
||
6446 | * |
||
6447 | * @param string[] $params |
||
6448 | * |
||
6449 | * @return string |
||
6450 | */ |
||
6451 | public function latestUserRegTime($params = array()) |
||
6452 | { |
||
6453 | return $this->getLatestUserData('regtime', $params); |
||
6454 | } |
||
6455 | |||
6456 | /** |
||
6457 | * Find the most recent user to log in. |
||
6458 | * |
||
6459 | * @param string[] $params |
||
6460 | * |
||
6461 | * @return string |
||
6462 | */ |
||
6463 | public function latestUserLoggedin($params = array()) |
||
6464 | { |
||
6465 | return $this->getLatestUserData('loggedin', $params); |
||
6466 | } |
||
6467 | |||
6468 | /** |
||
6469 | * Create a link to contact the webmaster. |
||
6470 | * |
||
6471 | * @return string |
||
6472 | */ |
||
6473 | public function contactWebmaster() |
||
6474 | { |
||
6475 | $user_id = $this->tree->getPreference('WEBMASTER_USER_ID'); |
||
6476 | $user = User::find($user_id); |
||
6477 | if ($user) { |
||
6478 | return Theme::theme()->contactLink($user); |
||
6479 | } else { |
||
6480 | return $user_id; |
||
6481 | } |
||
6482 | } |
||
6483 | |||
6484 | /** |
||
6485 | * Create a link to contact the genealogy contact. |
||
6486 | * |
||
6487 | * @return string |
||
6488 | */ |
||
6489 | public function contactGedcom() |
||
6490 | { |
||
6491 | $user_id = $this->tree->getPreference('CONTACT_USER_ID'); |
||
6492 | $user = User::find($user_id); |
||
6493 | if ($user) { |
||
6494 | return Theme::theme()->contactLink($user); |
||
6495 | } else { |
||
6496 | return $user_id; |
||
6497 | } |
||
6498 | } |
||
6499 | |||
6500 | /** |
||
6501 | * What is the current date on the server? |
||
6502 | * |
||
6503 | * @return string |
||
6504 | */ |
||
6505 | public function serverDate() |
||
6506 | { |
||
6507 | return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP)->display(); |
||
6508 | } |
||
6509 | |||
6510 | /** |
||
6511 | * What is the current time on the server (in 12 hour clock)? |
||
6512 | * |
||
6513 | * @return string |
||
6514 | */ |
||
6515 | public function serverTime() |
||
6516 | { |
||
6517 | return date('g:i a'); |
||
6518 | } |
||
6519 | |||
6520 | /** |
||
6521 | * What is the current time on the server (in 24 hour clock)? |
||
6522 | * |
||
6523 | * @return string |
||
6524 | */ |
||
6525 | public function serverTime24() |
||
6528 | } |
||
6529 | |||
6530 | /** |
||
6531 | * What is the timezone of the server. |
||
6532 | * |
||
6533 | * @return string |
||
6534 | */ |
||
6535 | public function serverTimezone() |
||
6536 | { |
||
6537 | return date('T'); |
||
6538 | } |
||
6539 | |||
6540 | /** |
||
6541 | * What is the client's date. |
||
6542 | * |
||
6543 | * @return string |
||
6544 | */ |
||
6545 | public function browserDate() |
||
6546 | { |
||
6547 | return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET)->display(); |
||
6548 | } |
||
6549 | |||
6550 | /** |
||
6551 | * What is the client's timestamp. |
||
6552 | * |
||
6553 | * @return string |
||
6554 | */ |
||
6555 | public function browserTime() |
||
6558 | } |
||
6559 | |||
6560 | /** |
||
6561 | * What is the browser's tiemzone. |
||
6562 | * |
||
6563 | * @return string |
||
6564 | */ |
||
6565 | public function browserTimezone() |
||
6568 | } |
||
6569 | |||
6570 | /** |
||
6571 | * What is the current version of webtrees. |
||
6572 | * |
||
6573 | * @return string |
||
6574 | */ |
||
6575 | public function webtreesVersion() |
||
6578 | } |
||
6579 | |||
6580 | /** |
||
6581 | * These functions provide access to hitcounter for use in the HTML block. |
||
6582 | * |
||
6583 | * @param string $page_name |
||
6584 | * @param string[] $params |
||
6585 | * |
||
6586 | * @return string |
||
6587 | */ |
||
6588 | private function hitCountQuery($page_name, $params) |
||
6589 | { |
||
6590 | if (is_array($params) && isset($params[0]) && $params[0] != '') { |
||
6591 | $page_parameter = $params[0]; |
||
6592 | } else { |
||
6593 | $page_parameter = ''; |
||
6594 | } |
||
6595 | |||
6596 | if ($page_name === null) { |
||
6597 | // index.php?ctype=gedcom |
||
6598 | $page_name = 'index.php'; |
||
6599 | $page_parameter = 'gedcom:' . ($page_parameter ? Tree::findByName($page_parameter)->getTreeId() : $this->tree->getTreeId()); |
||
6609 | } |
||
6610 | |||
6611 | /** |
||
6612 | * How many times has a page been viewed. |
||
6613 | * |
||
6614 | * @param string[] $params |
||
6615 | * |
||
6616 | * @return string |
||
6617 | */ |
||
6618 | public function hitCount($params = array()) |
||
6619 | { |
||
6620 | return $this->hitCountQuery(null, $params); |
||
6621 | } |
||
6622 | |||
6623 | /** |
||
6624 | * How many times has a page been viewed. |
||
6625 | * |
||
6626 | * @param string[] $params |
||
6627 | * |
||
6628 | * @return string |
||
6629 | */ |
||
6630 | public function hitCountUser($params = array()) |
||
6631 | { |
||
6632 | return $this->hitCountQuery('index.php', $params); |
||
6633 | } |
||
6634 | |||
6635 | /** |
||
6636 | * How many times has a page been viewed. |
||
6637 | * |
||
6638 | * @param string[] $params |
||
6639 | * |
||
6640 | * @return string |
||
6641 | */ |
||
6642 | public function hitCountIndi($params = array()) |
||
6643 | { |
||
6644 | return $this->hitCountQuery('individual.php', $params); |
||
6645 | } |
||
6646 | |||
6647 | /** |
||
6648 | * How many times has a page been viewed. |
||
6649 | * |
||
6650 | * @param string[] $params |
||
6651 | * |
||
6652 | * @return string |
||
6653 | */ |
||
6654 | public function hitCountFam($params = array()) |
||
6655 | { |
||
6656 | return $this->hitCountQuery('family.php', $params); |
||
6657 | } |
||
6658 | |||
6659 | /** |
||
6660 | * How many times has a page been viewed. |
||
6661 | * |
||
6662 | * @param string[] $params |
||
6663 | * |
||
6664 | * @return string |
||
6665 | */ |
||
6666 | public function hitCountSour($params = array()) |
||
6667 | { |
||
6668 | return $this->hitCountQuery('source.php', $params); |
||
6669 | } |
||
6670 | |||
6671 | /** |
||
6672 | * How many times has a page been viewed. |
||
6673 | * |
||
6674 | * @param string[] $params |
||
6675 | * |
||
6676 | * @return string |
||
6677 | */ |
||
6678 | public function hitCountRepo($params = array()) |
||
6679 | { |
||
6680 | return $this->hitCountQuery('repo.php', $params); |
||
6681 | } |
||
6682 | |||
6683 | /** |
||
6684 | * How many times has a page been viewed. |
||
6685 | * |
||
6686 | * @param string[] $params |
||
6687 | * |
||
6688 | * @return string |
||
6689 | */ |
||
6690 | public function hitCountNote($params = array()) |
||
6691 | { |
||
6692 | return $this->hitCountQuery('note.php', $params); |
||
6693 | } |
||
6694 | |||
6695 | /** |
||
6696 | * How many times has a page been viewed. |
||
6697 | * |
||
6698 | * @param string[] $params |
||
6699 | * |
||
6700 | * @return string |
||
6701 | */ |
||
6702 | public function hitCountObje($params = array()) |
||
6703 | { |
||
6704 | return $this->hitCountQuery('mediaviewer.php', $params); |
||
6705 | } |
||
6706 | |||
6707 | /** |
||
6708 | * Convert numbers to Google's custom encoding. |
||
6709 | * |
||
6710 | * @link http://bendodson.com/news/google-extended-encoding-made-easy |
||
6711 | * |
||
6712 | * @param int[] $a |
||
6713 | * |
||
6714 | * @return string |
||
6715 | */ |
||
6716 | private function arrayToExtendedEncoding($a) |
||
6717 | { |
||
6718 | $xencoding = WT_GOOGLE_CHART_ENCODING; |
||
6719 | |||
6720 | $encoding = ''; |
||
6721 | foreach ($a as $value) { |
||
6722 | if ($value < 0) { |
||
6723 | $value = 0; |
||
6724 | } |
||
6725 | $first = (int) ($value / 64); |
||
6726 | $second = $value % 64; |
||
6727 | $encoding .= $xencoding[(int) $first] . $xencoding[(int) $second]; |
||
6728 | } |
||
6729 | |||
6730 | return $encoding; |
||
6731 | } |
||
6732 | |||
6733 | /** |
||
6734 | * Callback function to compare totals. |
||
6735 | * |
||
6736 | * @param array $a |
||
6737 | * @param array $b |
||
6738 | * |
||
6739 | * @return int |
||
6740 | */ |
||
6741 | private function nameTotalSort($a, $b) |
||
6742 | { |
||
6743 | return $a['match'] - $b['match']; |
||
6744 | } |
||
6745 | |||
6746 | /** |
||
6747 | * Callback function to compare totals. |
||
6748 | * |
||
6749 | * @param array $a |
||
6750 | * @param array $b |
||
6751 | * |
||
6752 | * @return int |
||
6753 | */ |
||
6754 | private function nameTotalReverseSort($a, $b) |
||
6755 | { |
||
6756 | return $b['match'] - $a['match']; |
||
6757 | } |
||
6758 | |||
6759 | /** |
||
6760 | * Run an SQL query and cache the result. |
||
6761 | * |
||
6762 | * @param string $sql |
||
6763 | * |
||
6764 | * @return string[][] |
||
6765 | */ |
||
6766 | private function runSql($sql) |
||
6767 | { |
||
6768 | static $cache = array(); |
||
6769 | |||
6770 | $id = md5($sql); |
||
6771 | if (isset($cache[$id])) { |
||
6772 | return $cache[$id]; |
||
6773 | } |
||
6774 | $rows = Database::prepare($sql)->fetchAll(PDO::FETCH_ASSOC); |
||
6775 | $cache[$id] = $rows; |
||
6776 | |||
6777 | return $rows; |
||
6778 | } |
||
6779 | |||
6780 | /** |
||
6781 | * Find the favorites for the tree. |
||
6782 | * |
||
6783 | * @return string |
||
6784 | */ |
||
6785 | public function gedcomFavorites() |
||
6786 | { |
||
6787 | if (Module::getModuleByName('gedcom_favorites')) { |
||
6788 | $block = new FamilyTreeFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites'); |
||
6789 | |||
6790 | return $block->getBlock(0, false); |
||
6791 | } else { |
||
6792 | return ''; |
||
6793 | } |
||
6794 | } |
||
6795 | |||
6796 | /** |
||
6797 | * Find the favorites for the user. |
||
6798 | * |
||
6799 | * @return string |
||
6800 | */ |
||
6801 | public function userFavorites() |
||
6809 | } |
||
6810 | } |
||
6811 | |||
6812 | /** |
||
6813 | * Find the number of favorites for the tree. |
||
6814 | * |
||
6815 | * @return int |
||
6816 | */ |
||
6817 | public function totalGedcomFavorites() |
||
6818 | { |
||
6819 | if (Module::getModuleByName('gedcom_favorites')) { |
||
6820 | return count(FamilyTreeFavoritesModule::getFavorites($this->tree->getTreeId())); |
||
6821 | } else { |
||
6822 | return 0; |
||
6823 | } |
||
6824 | } |
||
6825 | |||
6826 | /** |
||
6827 | * Find the number of favorites for the user. |
||
6828 | * |
||
6829 | * @return int |
||
6830 | */ |
||
6831 | public function totalUserFavorites() |
||
6832 | { |
||
6833 | if (Module::getModuleByName('user_favorites')) { |
||
6834 | return count(UserFavoritesModule::getFavorites(Auth::id())); |
||
6835 | } else { |
||
6836 | return 0; |
||
6837 | } |
||
6838 | } |
||
6839 | |||
6840 | /** |
||
6841 | * Create any of the other blocks. |
||
6842 | * |
||
6843 | * Use as #callBlock:block_name# |
||
6844 | * |
||
6845 | * @param string[] $params |
||
6846 | * |
||
6847 | * @return string |
||
6848 | */ |
||
6849 | public function callBlock($params = array()) |
||
6850 | { |
||
6851 | global $ctype; |
||
6852 | |||
6853 | if (isset($params[0]) && $params[0] != '') { |
||
6854 | $block = $params[0]; |
||
6855 | } else { |
||
6856 | return ''; |
||
6857 | } |
||
6858 | $all_blocks = array(); |
||
6859 | foreach (Module::getActiveBlocks($this->tree) as $name => $active_block) { |
||
6860 | if ($ctype == 'user' && $active_block->isUserBlock() || $ctype == 'gedcom' && $active_block->isGedcomBlock()) { |
||
6861 | $all_blocks[$name] = $active_block; |
||
6862 | } |
||
6863 | } |
||
6864 | if (!array_key_exists($block, $all_blocks) || $block == 'html') { |
||
6865 | return ''; |
||
6866 | } |
||
6867 | // Build the config array |
||
6868 | array_shift($params); |
||
6869 | $cfg = array(); |
||
6870 | foreach ($params as $config) { |
||
6871 | $bits = explode('=', $config); |
||
6872 | if (count($bits) < 2) { |
||
6873 | continue; |
||
6874 | } |
||
6875 | $v = array_shift($bits); |
||
6876 | $cfg[$v] = implode('=', $bits); |
||
6877 | } |
||
6878 | $block = $all_blocks[$block]; |
||
6879 | $block_id = Filter::getInteger('block_id'); |
||
6880 | $content = $block->getBlock($block_id, false, $cfg); |
||
6881 | |||
6882 | return $content; |
||
6883 | } |
||
6884 | |||
6885 | /** |
||
6886 | * How many messages in the user's inbox. |
||
6887 | * |
||
6888 | * @return string |
||
6889 | */ |
||
6890 | public function totalUserMessages() |
||
6891 | { |
||
6892 | $total = (int) Database::prepare("SELECT COUNT(*) FROM `##message` WHERE user_id = ?") |
||
6893 | ->execute(array(Auth::id())) |
||
6894 | ->fetchOne(); |
||
6895 | |||
6896 | return I18N::number($total); |
||
6897 | } |
||
6898 | |||
6899 | /** |
||
6900 | * How many blog entries exist for this user. |
||
6901 | * |
||
6902 | * @return string |
||
6903 | */ |
||
6904 | public function totalUserJournal() |
||
6905 | { |
||
6906 | try { |
||
6907 | $number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE user_id = ?") |
||
6908 | ->execute(array(Auth::id())) |
||
6909 | ->fetchOne(); |
||
6910 | } catch (PDOException $ex) { |
||
6911 | // The module may not be installed, so the table may not exist. |
||
6912 | $number = 0; |
||
6913 | } |
||
6914 | |||
6915 | return I18N::number($number); |
||
6916 | } |
||
6917 | |||
6918 | /** |
||
6919 | * How many news items exist for this tree. |
||
6920 | * |
||
6921 | * @return string |
||
6922 | */ |
||
6923 | public function totalGedcomNews() |
||
6924 | { |
||
6925 | try { |
||
6926 | $number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE gedcom_id = ?") |
||
6927 | ->execute(array($this->tree->getTreeId())) |
||
6928 | ->fetchOne(); |
||
6929 | } catch (PDOException $ex) { |
||
6930 | // The module may not be installed, so the table may not exist. |
||
6931 | $number = 0; |
||
6932 | } |
||
6933 | |||
6934 | return I18N::number($number); |
||
6935 | } |
||
6936 | |||
6937 | /** |
||
6938 | * ISO3166 3 letter codes, with their 2 letter equivalent. |
||
6939 | * NOTE: this is not 1:1. ENG/SCO/WAL/NIR => GB |
||
6940 | * NOTE: this also includes champman codes and others. Should it? |
||
6941 | * |
||
6942 | * @return string[] |
||
6943 | */ |
||
6944 | public function iso3166() |
||
6989 | ); |
||
6990 | } |
||
6991 | |||
6992 | /** |
||
6993 | * Country codes and names |
||
6994 | * |
||
6995 | * @return string[] |
||
6996 | */ |
||
6997 | public function getAllCountries() |
||
6998 | { |
||
6999 | return array( |
||
7000 | '???' => /* I18N: Name of a country or state */ I18N::translate('Unknown'), |
||
7001 | 'ABW' => /* I18N: Name of a country or state */ I18N::translate('Aruba'), |
||
7002 | 'AFG' => /* I18N: Name of a country or state */ I18N::translate('Afghanistan'), |
||
7003 | 'AGO' => /* I18N: Name of a country or state */ I18N::translate('Angola'), |
||
7004 | 'AIA' => /* I18N: Name of a country or state */ I18N::translate('Anguilla'), |
||
7005 | 'ALA' => /* I18N: Name of a country or state */ I18N::translate('Aland Islands'), |
||
7006 | 'ALB' => /* I18N: Name of a country or state */ I18N::translate('Albania'), |
||
7007 | 'AND' => /* I18N: Name of a country or state */ I18N::translate('Andorra'), |
||
7008 | 'ARE' => /* I18N: Name of a country or state */ I18N::translate('United Arab Emirates'), |
||
7009 | 'ARG' => /* I18N: Name of a country or state */ I18N::translate('Argentina'), |
||
7010 | 'ARM' => /* I18N: Name of a country or state */ I18N::translate('Armenia'), |
||
7011 | 'ASM' => /* I18N: Name of a country or state */ I18N::translate('American Samoa'), |
||
7012 | 'ATA' => /* I18N: Name of a country or state */ I18N::translate('Antarctica'), |
||
7013 | 'ATF' => /* I18N: Name of a country or state */ I18N::translate('French Southern Territories'), |
||
7014 | 'ATG' => /* I18N: Name of a country or state */ I18N::translate('Antigua and Barbuda'), |
||
7015 | 'AUS' => /* I18N: Name of a country or state */ I18N::translate('Australia'), |
||
7016 | 'AUT' => /* I18N: Name of a country or state */ I18N::translate('Austria'), |
||
7017 | 'AZE' => /* I18N: Name of a country or state */ I18N::translate('Azerbaijan'), |
||
7018 | 'AZR' => /* I18N: Name of a country or state */ I18N::translate('Azores'), |
||
7019 | 'BDI' => /* I18N: Name of a country or state */ I18N::translate('Burundi'), |
||
7020 | 'BEL' => /* I18N: Name of a country or state */ I18N::translate('Belgium'), |
||
7021 | 'BEN' => /* I18N: Name of a country or state */ I18N::translate('Benin'), |
||
7022 | // BES => Bonaire, Sint Eustatius and Saba |
||
7023 | 'BFA' => /* I18N: Name of a country or state */ I18N::translate('Burkina Faso'), |
||
7024 | 'BGD' => /* I18N: Name of a country or state */ I18N::translate('Bangladesh'), |
||
7025 | 'BGR' => /* I18N: Name of a country or state */ I18N::translate('Bulgaria'), |
||
7026 | 'BHR' => /* I18N: Name of a country or state */ I18N::translate('Bahrain'), |
||
7027 | 'BHS' => /* I18N: Name of a country or state */ I18N::translate('Bahamas'), |
||
7028 | 'BIH' => /* I18N: Name of a country or state */ I18N::translate('Bosnia and Herzegovina'), |
||
7029 | // BLM => Saint Barthélemy |
||
7030 | 'BLR' => /* I18N: Name of a country or state */ I18N::translate('Belarus'), |
||
7031 | 'BLZ' => /* I18N: Name of a country or state */ I18N::translate('Belize'), |
||
7032 | 'BMU' => /* I18N: Name of a country or state */ I18N::translate('Bermuda'), |
||
7033 | 'BOL' => /* I18N: Name of a country or state */ I18N::translate('Bolivia'), |
||
7034 | 'BRA' => /* I18N: Name of a country or state */ I18N::translate('Brazil'), |
||
7035 | 'BRB' => /* I18N: Name of a country or state */ I18N::translate('Barbados'), |
||
7036 | 'BRN' => /* I18N: Name of a country or state */ I18N::translate('Brunei Darussalam'), |
||
7037 | 'BTN' => /* I18N: Name of a country or state */ I18N::translate('Bhutan'), |
||
7038 | 'BVT' => /* I18N: Name of a country or state */ I18N::translate('Bouvet Island'), |
||
7039 | 'BWA' => /* I18N: Name of a country or state */ I18N::translate('Botswana'), |
||
7040 | 'CAF' => /* I18N: Name of a country or state */ I18N::translate('Central African Republic'), |
||
7041 | 'CAN' => /* I18N: Name of a country or state */ I18N::translate('Canada'), |
||
7042 | 'CCK' => /* I18N: Name of a country or state */ I18N::translate('Cocos (Keeling) Islands'), |
||
7043 | 'CHE' => /* I18N: Name of a country or state */ I18N::translate('Switzerland'), |
||
7044 | 'CHL' => /* I18N: Name of a country or state */ I18N::translate('Chile'), |
||
7045 | 'CHN' => /* I18N: Name of a country or state */ I18N::translate('China'), |
||
7046 | 'CIV' => /* I18N: Name of a country or state */ I18N::translate('Cote d’Ivoire'), |
||
7047 | 'CMR' => /* I18N: Name of a country or state */ I18N::translate('Cameroon'), |
||
7048 | 'COD' => /* I18N: Name of a country or state */ I18N::translate('Democratic Republic of the Congo'), |
||
7049 | 'COG' => /* I18N: Name of a country or state */ I18N::translate('Republic of the Congo'), |
||
7050 | 'COK' => /* I18N: Name of a country or state */ I18N::translate('Cook Islands'), |
||
7051 | 'COL' => /* I18N: Name of a country or state */ I18N::translate('Colombia'), |
||
7052 | 'COM' => /* I18N: Name of a country or state */ I18N::translate('Comoros'), |
||
7053 | 'CPV' => /* I18N: Name of a country or state */ I18N::translate('Cape Verde'), |
||
7054 | 'CRI' => /* I18N: Name of a country or state */ I18N::translate('Costa Rica'), |
||
7055 | 'CUB' => /* I18N: Name of a country or state */ I18N::translate('Cuba'), |
||
7056 | // CUW => Curaçao |
||
7057 | 'CXR' => /* I18N: Name of a country or state */ I18N::translate('Christmas Island'), |
||
7058 | 'CYM' => /* I18N: Name of a country or state */ I18N::translate('Cayman Islands'), |
||
7059 | 'CYP' => /* I18N: Name of a country or state */ I18N::translate('Cyprus'), |
||
7060 | 'CZE' => /* I18N: Name of a country or state */ I18N::translate('Czech Republic'), |
||
7061 | 'DEU' => /* I18N: Name of a country or state */ I18N::translate('Germany'), |
||
7062 | 'DJI' => /* I18N: Name of a country or state */ I18N::translate('Djibouti'), |
||
7063 | 'DMA' => /* I18N: Name of a country or state */ I18N::translate('Dominica'), |
||
7064 | 'DNK' => /* I18N: Name of a country or state */ I18N::translate('Denmark'), |
||
7065 | 'DOM' => /* I18N: Name of a country or state */ I18N::translate('Dominican Republic'), |
||
7066 | 'DZA' => /* I18N: Name of a country or state */ I18N::translate('Algeria'), |
||
7067 | 'ECU' => /* I18N: Name of a country or state */ I18N::translate('Ecuador'), |
||
7068 | 'EGY' => /* I18N: Name of a country or state */ I18N::translate('Egypt'), |
||
7069 | 'ENG' => /* I18N: Name of a country or state */ I18N::translate('England'), |
||
7070 | 'ERI' => /* I18N: Name of a country or state */ I18N::translate('Eritrea'), |
||
7071 | 'ESH' => /* I18N: Name of a country or state */ I18N::translate('Western Sahara'), |
||
7072 | 'ESP' => /* I18N: Name of a country or state */ I18N::translate('Spain'), |
||
7073 | 'EST' => /* I18N: Name of a country or state */ I18N::translate('Estonia'), |
||
7074 | 'ETH' => /* I18N: Name of a country or state */ I18N::translate('Ethiopia'), |
||
7075 | 'FIN' => /* I18N: Name of a country or state */ I18N::translate('Finland'), |
||
7076 | 'FJI' => /* I18N: Name of a country or state */ I18N::translate('Fiji'), |
||
7077 | 'FLD' => /* I18N: Name of a country or state */ I18N::translate('Flanders'), |
||
7078 | 'FLK' => /* I18N: Name of a country or state */ I18N::translate('Falkland Islands'), |
||
7079 | 'FRA' => /* I18N: Name of a country or state */ I18N::translate('France'), |
||
7080 | 'FRO' => /* I18N: Name of a country or state */ I18N::translate('Faroe Islands'), |
||
7081 | 'FSM' => /* I18N: Name of a country or state */ I18N::translate('Micronesia'), |
||
7082 | 'GAB' => /* I18N: Name of a country or state */ I18N::translate('Gabon'), |
||
7083 | 'GBR' => /* I18N: Name of a country or state */ I18N::translate('United Kingdom'), |
||
7084 | 'GEO' => /* I18N: Name of a country or state */ I18N::translate('Georgia'), |
||
7085 | 'GGY' => /* I18N: Name of a country or state */ I18N::translate('Guernsey'), |
||
7086 | 'GHA' => /* I18N: Name of a country or state */ I18N::translate('Ghana'), |
||
7087 | 'GIB' => /* I18N: Name of a country or state */ I18N::translate('Gibraltar'), |
||
7088 | 'GIN' => /* I18N: Name of a country or state */ I18N::translate('Guinea'), |
||
7089 | 'GLP' => /* I18N: Name of a country or state */ I18N::translate('Guadeloupe'), |
||
7090 | 'GMB' => /* I18N: Name of a country or state */ I18N::translate('Gambia'), |
||
7091 | 'GNB' => /* I18N: Name of a country or state */ I18N::translate('Guinea-Bissau'), |
||
7092 | 'GNQ' => /* I18N: Name of a country or state */ I18N::translate('Equatorial Guinea'), |
||
7093 | 'GRC' => /* I18N: Name of a country or state */ I18N::translate('Greece'), |
||
7094 | 'GRD' => /* I18N: Name of a country or state */ I18N::translate('Grenada'), |
||
7095 | 'GRL' => /* I18N: Name of a country or state */ I18N::translate('Greenland'), |
||
7096 | 'GTM' => /* I18N: Name of a country or state */ I18N::translate('Guatemala'), |
||
7097 | 'GUF' => /* I18N: Name of a country or state */ I18N::translate('French Guiana'), |
||
7098 | 'GUM' => /* I18N: Name of a country or state */ I18N::translate('Guam'), |
||
7099 | 'GUY' => /* I18N: Name of a country or state */ I18N::translate('Guyana'), |
||
7100 | 'HKG' => /* I18N: Name of a country or state */ I18N::translate('Hong Kong'), |
||
7101 | 'HMD' => /* I18N: Name of a country or state */ I18N::translate('Heard Island and McDonald Islands'), |
||
7102 | 'HND' => /* I18N: Name of a country or state */ I18N::translate('Honduras'), |
||
7103 | 'HRV' => /* I18N: Name of a country or state */ I18N::translate('Croatia'), |
||
7104 | 'HTI' => /* I18N: Name of a country or state */ I18N::translate('Haiti'), |
||
7105 | 'HUN' => /* I18N: Name of a country or state */ I18N::translate('Hungary'), |
||
7106 | 'IDN' => /* I18N: Name of a country or state */ I18N::translate('Indonesia'), |
||
7107 | 'IND' => /* I18N: Name of a country or state */ I18N::translate('India'), |
||
7108 | 'IOM' => /* I18N: Name of a country or state */ I18N::translate('Isle of Man'), |
||
7109 | 'IOT' => /* I18N: Name of a country or state */ I18N::translate('British Indian Ocean Territory'), |
||
7110 | 'IRL' => /* I18N: Name of a country or state */ I18N::translate('Ireland'), |
||
7111 | 'IRN' => /* I18N: Name of a country or state */ I18N::translate('Iran'), |
||
7112 | 'IRQ' => /* I18N: Name of a country or state */ I18N::translate('Iraq'), |
||
7113 | 'ISL' => /* I18N: Name of a country or state */ I18N::translate('Iceland'), |
||
7114 | 'ISR' => /* I18N: Name of a country or state */ I18N::translate('Israel'), |
||
7115 | 'ITA' => /* I18N: Name of a country or state */ I18N::translate('Italy'), |
||
7116 | 'JAM' => /* I18N: Name of a country or state */ I18N::translate('Jamaica'), |
||
7117 | //'JEY' => Jersey |
||
7118 | 'JOR' => /* I18N: Name of a country or state */ I18N::translate('Jordan'), |
||
7119 | 'JPN' => /* I18N: Name of a country or state */ I18N::translate('Japan'), |
||
7120 | 'KAZ' => /* I18N: Name of a country or state */ I18N::translate('Kazakhstan'), |
||
7121 | 'KEN' => /* I18N: Name of a country or state */ I18N::translate('Kenya'), |
||
7122 | 'KGZ' => /* I18N: Name of a country or state */ I18N::translate('Kyrgyzstan'), |
||
7123 | 'KHM' => /* I18N: Name of a country or state */ I18N::translate('Cambodia'), |
||
7124 | 'KIR' => /* I18N: Name of a country or state */ I18N::translate('Kiribati'), |
||
7125 | 'KNA' => /* I18N: Name of a country or state */ I18N::translate('Saint Kitts and Nevis'), |
||
7126 | 'KOR' => /* I18N: Name of a country or state */ I18N::translate('Korea'), |
||
7127 | 'KWT' => /* I18N: Name of a country or state */ I18N::translate('Kuwait'), |
||
7128 | 'LAO' => /* I18N: Name of a country or state */ I18N::translate('Laos'), |
||
7129 | 'LBN' => /* I18N: Name of a country or state */ I18N::translate('Lebanon'), |
||
7130 | 'LBR' => /* I18N: Name of a country or state */ I18N::translate('Liberia'), |
||
7131 | 'LBY' => /* I18N: Name of a country or state */ I18N::translate('Libya'), |
||
7132 | 'LCA' => /* I18N: Name of a country or state */ I18N::translate('Saint Lucia'), |
||
7133 | 'LIE' => /* I18N: Name of a country or state */ I18N::translate('Liechtenstein'), |
||
7134 | 'LKA' => /* I18N: Name of a country or state */ I18N::translate('Sri Lanka'), |
||
7135 | 'LSO' => /* I18N: Name of a country or state */ I18N::translate('Lesotho'), |
||
7136 | 'LTU' => /* I18N: Name of a country or state */ I18N::translate('Lithuania'), |
||
7137 | 'LUX' => /* I18N: Name of a country or state */ I18N::translate('Luxembourg'), |
||
7138 | 'LVA' => /* I18N: Name of a country or state */ I18N::translate('Latvia'), |
||
7139 | 'MAC' => /* I18N: Name of a country or state */ I18N::translate('Macau'), |
||
7140 | // MAF => Saint Martin |
||
7141 | 'MAR' => /* I18N: Name of a country or state */ I18N::translate('Morocco'), |
||
7142 | 'MCO' => /* I18N: Name of a country or state */ I18N::translate('Monaco'), |
||
7143 | 'MDA' => /* I18N: Name of a country or state */ I18N::translate('Moldova'), |
||
7144 | 'MDG' => /* I18N: Name of a country or state */ I18N::translate('Madagascar'), |
||
7145 | 'MDV' => /* I18N: Name of a country or state */ I18N::translate('Maldives'), |
||
7146 | 'MEX' => /* I18N: Name of a country or state */ I18N::translate('Mexico'), |
||
7147 | 'MHL' => /* I18N: Name of a country or state */ I18N::translate('Marshall Islands'), |
||
7148 | 'MKD' => /* I18N: Name of a country or state */ I18N::translate('Macedonia'), |
||
7149 | 'MLI' => /* I18N: Name of a country or state */ I18N::translate('Mali'), |
||
7150 | 'MLT' => /* I18N: Name of a country or state */ I18N::translate('Malta'), |
||
7151 | 'MMR' => /* I18N: Name of a country or state */ I18N::translate('Myanmar'), |
||
7152 | 'MNG' => /* I18N: Name of a country or state */ I18N::translate('Mongolia'), |
||
7153 | 'MNP' => /* I18N: Name of a country or state */ I18N::translate('Northern Mariana Islands'), |
||
7154 | 'MNT' => /* I18N: Name of a country or state */ I18N::translate('Montenegro'), |
||
7155 | 'MOZ' => /* I18N: Name of a country or state */ I18N::translate('Mozambique'), |
||
7156 | 'MRT' => /* I18N: Name of a country or state */ I18N::translate('Mauritania'), |
||
7157 | 'MSR' => /* I18N: Name of a country or state */ I18N::translate('Montserrat'), |
||
7158 | 'MTQ' => /* I18N: Name of a country or state */ I18N::translate('Martinique'), |
||
7159 | 'MUS' => /* I18N: Name of a country or state */ I18N::translate('Mauritius'), |
||
7160 | 'MWI' => /* I18N: Name of a country or state */ I18N::translate('Malawi'), |
||
7257 | ); |
||
7258 | } |
||
7259 | |||
7260 | /** |
||
7261 | * Century name, English => 21st, Polish => XXI, etc. |
||
7262 | * |
||
7263 | * @param int $century |
||
7264 | * |
||
7265 | * @return string |
||
7266 | */ |
||
7267 | private function centuryName($century) |
||
7268 | { |
||
7269 | if ($century < 0) { |
||
7270 | return str_replace(-$century, self::centuryName(-$century), /* I18N: BCE=Before the Common Era, for Julian years < 0. See http://en.wikipedia.org/wiki/Common_Era */ |
||
7271 | I18N::translate('%s BCE', I18N::number(-$century))); |
||
7272 | } |
||
7273 | // The current chart engine (Google charts) can't handle <sup></sup> markup |
||
7274 | switch ($century) { |
||
7275 | case 21: |
||
7276 | return strip_tags(I18N::translateContext('CENTURY', '21st')); |
||
7277 | case 20: |
||
7278 | return strip_tags(I18N::translateContext('CENTURY', '20th')); |
||
7279 | case 19: |
||
7280 | return strip_tags(I18N::translateContext('CENTURY', '19th')); |
||
7281 | case 18: |
||
7282 | return strip_tags(I18N::translateContext('CENTURY', '18th')); |
||
7283 | case 17: |
||
7284 | return strip_tags(I18N::translateContext('CENTURY', '17th')); |
||
7319 | } |
||
7320 | } |
||
7321 | } |
||
7322 |