Passed
Push — dev ( 8976f1...f79383 )
by Greg
07:34
created

FunctionsPrint::printAddNewFact()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 85
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 64
nc 6
nop 1
dl 0
loc 85
rs 8.4743
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2021 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Functions;
21
22
use Fisharebest\Webtrees\Age;
23
use Fisharebest\Webtrees\Date;
24
use Fisharebest\Webtrees\Fact;
25
use Fisharebest\Webtrees\Family;
26
use Fisharebest\Webtrees\Gedcom;
27
use Fisharebest\Webtrees\GedcomRecord;
28
use Fisharebest\Webtrees\I18N;
29
use Fisharebest\Webtrees\Individual;
30
use Fisharebest\Webtrees\Note;
31
use Fisharebest\Webtrees\Registry;
32
use Fisharebest\Webtrees\Tree;
33
use Illuminate\Support\Str;
34
use Ramsey\Uuid\Uuid;
35
36
use function array_filter;
37
use function e;
38
use function explode;
39
use function in_array;
40
use function preg_match;
41
use function preg_match_all;
42
use function str_contains;
43
use function strip_tags;
44
use function strlen;
45
use function strpos;
46
use function substr;
47
use function view;
48
49
use const PREG_SET_ORDER;
50
51
/**
52
 * Class FunctionsPrint - common functions
53
 *
54
 * @deprecated since 2.0.6.  Will be removed in 2.1.0
55
 */
56
class FunctionsPrint
57
{
58
    /**
59
     * print a note record
60
     *
61
     * @param Tree   $tree
62
     * @param string $text
63
     * @param int    $nlevel the level of the note record
64
     * @param string $nrec   the note record to print
65
     *
66
     * @return string
67
     */
68
    private static function printNoteRecord(Tree $tree, string $text, int $nlevel, string $nrec): string
69
    {
70
        $element = Registry::elementFactory()->make('NOTE:CONC');
71
72
        $text .= Functions::getCont($nlevel, $nrec);
73
74
        if (preg_match('/^0 @(' . Gedcom::REGEX_XREF . ')@ NOTE/', $nrec, $match)) {
75
            // Shared note.
76
            $note = Registry::noteFactory()->make($match[1], $tree);
77
            // It must exist.
78
            assert($note instanceof Note);
79
80
            $label      = I18N::translate('Shared note');
81
            $html       = $element->value($note->getNote(), $tree);
82
            $first_line = '<a href="' . e($note->url()) . '">' . $note->fullName() . '</a>';
83
84
            $one_line_only = strip_tags($note->fullName()) === strip_tags($note->getNote());
85
        } else {
86
            // Inline note.
87
            $label = I18N::translate('Note');
88
            $html  = $element->value($text, $tree);
89
90
            [$first_line] = explode("\n", strip_tags($html));
91
            // Use same logic as note objects
92
            $first_line = Str::limit($first_line, 100, I18N::translate('…'));
93
94
            $one_line_only = !str_contains($text, "\n") && mb_strlen($text) <= 100;
95
        }
96
97
        if ($one_line_only) {
98
            return
99
                '<div class="fact_NOTE">' .
100
                I18N::translate(
101
                    '<span class="label">%1$s:</span> <span class="field" dir="auto">%2$s</span>',
102
                    $label,
103
                    $html
104
                ) .
105
                '</div>';
106
        }
107
108
        $id       = 'collapse-' . Uuid::uuid4()->toString();
109
        $expanded = (bool) $tree->getPreference('EXPAND_NOTES');
110
111
        return
112
            '<div class="fact_NOTE">' .
113
            '<a href="#' . e($id) . '" role="button" data-bs-toggle="collapse" aria-controls="' . e($id) . '" aria-expanded="' . ($expanded ? 'true' : 'false') . '">' .
114
            view('icons/expand') .
115
            view('icons/collapse') .
116
            '</a>' .
117
            '<span class="label">' . $label . ':</span> ' .
118
            $first_line .
119
            '</div>' .
120
            '<div id="' . e($id) . '" class="collapse ' . ($expanded ? 'show' : '') . '">' .
121
            $html .
122
            '</div>';
123
    }
124
125
    /**
126
     * Print all of the notes in this fact record
127
     *
128
     * @param Tree   $tree
129
     * @param string $factrec The fact to print the notes from
130
     * @param int    $level   The level of the notes
131
     *
132
     * @return string HTML
133
     */
134
    public static function printFactNotes(Tree $tree, string $factrec, int $level): string
135
    {
136
        $data          = '';
137
        $previous_spos = 0;
138
        $nlevel        = $level + 1;
139
        $ct            = preg_match_all("/$level NOTE (.*)/", $factrec, $match, PREG_SET_ORDER);
140
        for ($j = 0; $j < $ct; $j++) {
141
            $spos1 = strpos($factrec, $match[$j][0], $previous_spos);
142
            $spos2 = strpos($factrec . "\n$level", "\n$level", $spos1 + 1);
143
            if (!$spos2) {
144
                $spos2 = strlen($factrec);
145
            }
146
            $previous_spos = $spos2;
147
            $nrec          = substr($factrec, $spos1, $spos2 - $spos1);
148
            if (!isset($match[$j][1])) {
149
                $match[$j][1] = '';
150
            }
151
            if (!preg_match('/^@(' . Gedcom::REGEX_XREF . ')@$/', $match[$j][1], $nmatch)) {
152
                $data .= self::printNoteRecord($tree, $match[$j][1], $nlevel, $nrec);
153
            } else {
154
                $note = Registry::noteFactory()->make($nmatch[1], $tree);
155
                if ($note) {
156
                    if ($note->canShow()) {
157
                        $noterec = $note->gedcom();
158
                        $nt      = preg_match("/0 @$nmatch[1]@ NOTE (.*)/", $noterec, $n1match);
159
                        $data    .= self::printNoteRecord($tree, $nt > 0 ? $n1match[1] : '', 1, $noterec);
160
                    }
161
                } else {
162
                    $data = '<div class="fact_NOTE"><span class="label">' . I18N::translate('Note') . '</span>: <span class="field error">' . $nmatch[1] . '</span></div>';
163
                }
164
            }
165
        }
166
167
        return $data;
168
    }
169
170
    /**
171
     * Format age of parents in HTML
172
     *
173
     * @param Individual $person child
174
     * @param Date       $birth_date
175
     *
176
     * @return string HTML
177
     */
178
    public static function formatParentsAges(Individual $person, Date $birth_date): string
179
    {
180
        $html     = '';
181
        $families = $person->childFamilies();
182
        // Multiple sets of parents (e.g. adoption) cause complications, so ignore.
183
        if ($birth_date->isOK() && $families->count() === 1) {
184
            $family = $families->first();
185
            foreach ($family->spouses() as $parent) {
186
                if ($parent->getBirthDate()->isOK()) {
187
                    $sex      = '<small>' . view('icons/sex', ['sex' => $parent->sex()]) . '</small>';
188
                    $age      = new Age($parent->getBirthDate(), $birth_date);
189
                    $deatdate = $parent->getDeathDate();
190
                    switch ($parent->sex()) {
191
                        case 'F':
192
                            // Highlight mothers who die in childbirth or shortly afterwards
193
                            if ($deatdate->isOK() && $deatdate->maximumJulianDay() < $birth_date->minimumJulianDay() + 90) {
194
                                $html .= ' <span title="' . I18N::translate('Death of a mother') . '" class="parentdeath">' . $sex . I18N::number($age->ageYears()) . '</span>';
195
                            } else {
196
                                $html .= ' <span title="' . I18N::translate('Mother’s age') . '">' . $sex . I18N::number($age->ageYears()) . '</span>';
197
                            }
198
                            break;
199
                        case 'M':
200
                            // Highlight fathers who die before the birth
201
                            if ($deatdate->isOK() && $deatdate->maximumJulianDay() < $birth_date->minimumJulianDay()) {
202
                                $html .= ' <span title="' . I18N::translate('Death of a father') . '" class="parentdeath">' . $sex . I18N::number($age->ageYears()) . '</span>';
203
                            } else {
204
                                $html .= ' <span title="' . I18N::translate('Father’s age') . '">' . $sex . I18N::number($age->ageYears()) . '</span>';
205
                            }
206
                            break;
207
                        default:
208
                            $html .= ' <span title="' . I18N::translate('Parent’s age') . '">' . $sex . I18N::number($age->ageYears()) . '</span>';
209
                            break;
210
                    }
211
                }
212
            }
213
            if ($html) {
214
                $html = '<span class="age">' . $html . '</span>';
215
            }
216
        }
217
218
        return $html;
219
    }
220
221
    /**
222
     * Print fact DATE/TIME
223
     *
224
     * @param Fact         $event  event containing the date/age
225
     * @param GedcomRecord $record the person (or couple) whose ages should be printed
226
     * @param bool         $anchor option to print a link to calendar
227
     * @param bool         $time   option to print TIME value
228
     *
229
     * @return string
230
     */
231
    public static function formatFactDate(Fact $event, GedcomRecord $record, bool $anchor, bool $time): string
232
    {
233
        $element_factory = Registry::elementFactory();
234
235
        $factrec = $event->gedcom();
236
        $html    = '';
237
        // Recorded age
238
        if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
239
            $fact_age = $element_factory->make($event->tag() . ':AGE')->value($match[1], $record->tree());
240
        } else {
241
            $fact_age = '';
242
        }
243
        if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
244
            $husb_age = $element_factory->make($event->tag() . ':HUSB:AGE')->value($match[1], $record->tree());
245
        } else {
246
            $husb_age = '';
247
        }
248
        if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
249
            $wife_age = $element_factory->make($event->tag() . ':WIFE:AGE')->value($match[1], $record->tree());
250
        } else {
251
            $wife_age = '';
252
        }
253
254
        // Calculated age
255
        [, $fact] = explode(':', $event->tag());
256
257
        if (preg_match('/\n2 DATE (.+)/', $factrec, $match)) {
258
            $date = new Date($match[1]);
259
            $html .= ' ' . $date->display($anchor ? $record->tree() : null, null, $anchor);
260
            // time
261
            if ($time && preg_match('/\n3 TIME (.+)/', $factrec, $match)) {
262
                $html .= ' – <span class="date">' . $match[1] . '</span>';
263
            }
264
            if ($record instanceof Individual) {
265
                if (
266
                    in_array($fact, Gedcom::BIRTH_EVENTS, true) &&
267
                    $record === $event->record() &&
0 ignored issues
show
introduced by
The condition $record === $event->record() is always false.
Loading history...
268
                    $record->tree()->getPreference('SHOW_PARENTS_AGE') === '1'
269
                ) {
270
                    // age of parents at child birth
271
                    $html .= self::formatParentsAges($record, $date);
272
                }
273
                if ($fact !== 'BIRT' && $fact !== 'CHAN' && $fact !== '_TODO') {
274
                    // age at event
275
                    $birth_date = $record->getBirthDate();
276
                    // Can't use getDeathDate(), as this also gives BURI/CREM events, which
277
                    // wouldn't give the correct "days after death" result for people with
278
                    // no DEAT.
279
                    $death_event = $record->facts(['DEAT'])->first();
280
                    if ($death_event instanceof Fact) {
281
                        $death_date = $death_event->date();
282
                    } else {
283
                        $death_date = new Date('');
284
                    }
285
                    $ageText = '';
286
                    if ($fact === 'DEAT' || Date::compare($date, $death_date) <= 0 || !$record->isDead()) {
287
                        // Before death, print age
288
                        $age = (string) new Age($birth_date, $date);
289
290
                        // Only show calculated age if it differs from recorded age
291
                        if ($age !== '') {
292
                            if (
293
                                $fact_age !== '' && !str_starts_with($fact_age, $age) ||
294
                                $fact_age === '' && $husb_age === '' && $wife_age === '' ||
295
                                $husb_age !== '' && !str_starts_with($husb_age, $age) && $record->sex() === 'M' ||
296
                                $wife_age !== '' && !str_starts_with($wife_age, $age) && $record->sex() === 'F'
297
                            ) {
298
                                switch ($record->sex()) {
299
                                    case 'M':
300
                                        /* I18N: The age of an individual at a given date */
301
                                        $ageText = I18N::translateContext('Male', '(aged %s)', $age);
302
                                        break;
303
                                    case 'F':
304
                                        /* I18N: The age of an individual at a given date */
305
                                        $ageText = I18N::translateContext('Female', '(aged %s)', $age);
306
                                        break;
307
                                    default:
308
                                        /* I18N: The age of an individual at a given date */
309
                                        $ageText = I18N::translate('(aged %s)', $age);
310
                                        break;
311
                                }
312
                            }
313
                        }
314
                    }
315
                    if ($fact !== 'DEAT' && $death_date->isOK() && Date::compare($death_date, $date) <= 0) {
316
                        $death_day = $death_date->minimumDate()->day();
317
                        $event_day = $date->minimumDate()->day();
318
                        if ($death_day !== 0 && $event_day !== 0 && Date::compare($death_date, $date) === 0) {
319
                            // On the exact date of death?
320
                            // NOTE: this path is never reached.  Keep the code (translation) in case
321
                            // we decide to re-introduce it.
322
                            $ageText = I18N::translate('(on the date of death)');
323
                        } else {
324
                            // After death
325
                            $age = (string) new Age($death_date, $date);
326
                            $ageText = I18N::translate('(%s after death)', $age);
327
                        }
328
                        // Family events which occur after death are probably errors
329
                        if ($event->record() instanceof Family) {
330
                            $ageText .= view('icons/warning');
331
                        }
332
                    }
333
                    if ($ageText !== '') {
334
                        $html .= ' <span class="age">' . $ageText . '</span>';
335
                    }
336
                }
337
            }
338
        }
339
        // print gedcom ages
340
        $age_labels = [
341
            I18N::translate('Age')     => $fact_age,
342
            I18N::translate('Husband') => $husb_age,
343
            I18N::translate('Wife')    => $wife_age,
344
        ];
345
346
        foreach (array_filter($age_labels) as $label => $age) {
347
            $html .= ' <span class="label">' . $label . ':</span> <span class="age">' . $age . '</span>';
348
        }
349
350
        return $html;
351
    }
352
}
353