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

FunctionsPrintFacts::printMainNotes()   C

Complexity

Conditions 13
Paths 170

Size

Total Lines 63
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 46
nc 170
nop 2
dl 0
loc 63
rs 6.0333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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\Fact;
23
use Fisharebest\Webtrees\I18N;
24
use Fisharebest\Webtrees\Media;
25
use Fisharebest\Webtrees\Registry;
26
use Fisharebest\Webtrees\Tree;
27
use Ramsey\Uuid\Uuid;
28
29
use function count;
30
use function e;
31
use function ob_get_clean;
32
use function ob_start;
33
use function preg_match_all;
34
use function preg_replace;
35
use function str_contains;
36
use function strlen;
37
use function strpos;
38
use function substr;
39
use function trim;
40
use function view;
41
42
use const PREG_SET_ORDER;
43
44
/**
45
 * Class FunctionsPrintFacts - common functions
46
 *
47
 * @deprecated since 2.0.6.  Will be removed in 2.1.0
48
 */
49
class FunctionsPrintFacts
50
{
51
    /**
52
     * print a source linked to a fact (2 SOUR)
53
     * this function is called by the FunctionsPrintFacts::print_fact function and other functions to
54
     * print any source information attached to the fact
55
     *
56
     * @param Tree   $tree
57
     * @param string $factrec The fact record to look for sources in
58
     * @param int    $level   The level to look for sources at
59
     *
60
     * @return string HTML text
61
     */
62
    public static function printFactSources(Tree $tree, string $factrec, int $level): string
63
    {
64
        $data   = '';
65
        $nlevel = $level + 1;
66
67
        // Systems not using source records
68
        // The old style is not supported when entering or editing sources, but may be found in imported trees.
69
        // Also, the old style sources allow histo.* files to use tree independent source citations, which
70
        // will display nicely when markdown is used.
71
        $ct = preg_match_all('/' . $level . ' SOUR (.*)((?:\n\d CONT.*)*)/', $factrec, $match, PREG_SET_ORDER);
72
        for ($j = 0; $j < $ct; $j++) {
73
            if (!str_contains($match[$j][1], '@')) {
74
                $source = e($match[$j][1] . preg_replace('/\n\d CONT ?/', "\n", $match[$j][2]));
75
                $data .= '<div class="fact_SOUR"><span class="label">' . I18N::translate('Source') . ':</span> <span class="field" dir="auto">';
76
77
                if ($tree->getPreference('FORMAT_TEXT') === 'markdown') {
78
                    $data .= '<div class="markdown" dir="auto">';
79
                    $data .= Registry::markdownFactory()->markdown($source, $tree);
80
                    $data .= '</div>';
81
                } else {
82
                    $data .= '<div class="markdown" dir="auto">';
83
                    $data .= Registry::markdownFactory()->autolink($source, $tree);
84
                    $data .= '</div>';
85
                }
86
87
                $data .= '</span></div>';
88
            }
89
        }
90
        // Find source for each fact
91
        $ct    = preg_match_all("/$level SOUR @(.*)@/", $factrec, $match, PREG_SET_ORDER);
92
        $spos2 = 0;
93
        for ($j = 0; $j < $ct; $j++) {
94
            $sid    = $match[$j][1];
95
            $source = Registry::sourceFactory()->make($sid, $tree);
96
            if ($source) {
97
                if ($source->canShow()) {
98
                    $spos1 = strpos($factrec, "$level SOUR @" . $sid . '@', $spos2);
99
                    $spos2 = strpos($factrec, "\n$level", $spos1);
100
                    if (!$spos2) {
101
                        $spos2 = strlen($factrec);
102
                    }
103
104
                    $srec = substr($factrec, $spos1, $spos2 - $spos1);
105
                    $lt   = preg_match_all("/$nlevel \w+/", $srec, $matches);
106
                    $data .= '<div class="fact_SOUR">';
107
                    $id       = 'collapse-' . Uuid::uuid4()->toString();
108
                    $expanded = (bool) $tree->getPreference('EXPAND_SOURCES');
109
110
                    if ($lt > 0) {
111
                        $data .= '<a href="#' . e($id) . '" role="button" data-bs-toggle="collapse" aria-controls="' . e($id) . '" aria-expanded="' . ($expanded ? 'true' : 'false') . '">';
112
                        $data .= view('icons/expand');
113
                        $data .= view('icons/collapse');
114
                        $data .= '</a>';
115
                    } elseif ($ct > 1) {
116
                        $data .= view('icons/spacer');
117
                    }
118
119
                    $value = '<a href="' . e($source->url()) . '">' . $source->fullName() . '</a>';
120
                    $data .= I18N::translate('<span class="label">%1$s:</span> <span class="field" dir="auto">%2$s</span>', I18N::translate('Source'), $value);
121
                    $data .= '</div>';
122
                    $data .= '<div id="' . e($id) . '" class="collapse ' . ($expanded ? 'show' : '') . '">';
123
                    $data .= self::printSourceStructure($tree, self::getSourceStructure($srec));
124
                    $data .= '<div class="indent">';
125
                    ob_start();
126
                    self::printMediaLinks($tree, $srec, $nlevel);
127
                    $data .= ob_get_clean();
128
                    $data .= FunctionsPrint::printFactNotes($tree, $srec, $nlevel);
129
                    $data .= '</div>';
130
                    $data .= '</div>';
131
                }
132
            } else {
133
                $value = '<span class="error">' . $sid . '</span>';
134
                $data .= I18N::translate('<span class="label">%1$s:</span> <span class="field" dir="auto">%2$s</span>', I18N::translate('Source'), $value);
135
            }
136
        }
137
138
        return $data;
139
    }
140
141
    /**
142
     * Print the links to media objects
143
     *
144
     * @param Tree   $tree
145
     * @param string $factrec
146
     * @param int    $level
147
     *
148
     * @return void
149
     */
150
    public static function printMediaLinks(Tree $tree, string $factrec, int $level): void
151
    {
152
        $nlevel = $level + 1;
153
        if (preg_match_all("/$level OBJE @(.*)@/", $factrec, $omatch, PREG_SET_ORDER) === 0) {
154
            return;
155
        }
156
        $objectNum = 0;
157
        while ($objectNum < count($omatch)) {
158
            $media_id = $omatch[$objectNum][1];
159
            $media    = Registry::mediaFactory()->make($media_id, $tree);
160
            if ($media) {
161
                if ($media->canShow()) {
162
                    echo '<div class="d-flex align-items-center"><div class="p-1">';
163
                    foreach ($media->mediaFiles() as $media_file) {
164
                        echo $media_file->displayImage(100, 100, 'contain', []);
165
                    }
166
                    echo '</div>';
167
                    echo '<div>';
168
                    echo '<a href="', e($media->url()), '">', $media->fullName(), '</a>';
169
                    // NOTE: echo the notes of the media
170
                    echo '<p>';
171
                    echo FunctionsPrint::printFactNotes($tree, $media->gedcom(), 1);
172
                    //-- print spouse name for marriage events
173
                    echo FunctionsPrint::printFactNotes($tree, $media->gedcom(), $nlevel);
174
                    echo self::printFactSources($tree, $media->gedcom(), $nlevel);
175
                    echo '</div>'; //close div "media-display-title"
176
                    echo '</div>'; //close div "media-display"
177
                }
178
            } elseif ($tree->getPreference('HIDE_GEDCOM_ERRORS') === '1') {
179
                echo '<p class="alert alert-danger">', $media_id, '</p>';
180
            }
181
            $objectNum++;
182
        }
183
    }
184
185
    /**
186
     * Print a row for the sources tab on the individual page.
187
     *
188
     * @param Fact $fact
189
     * @param int  $level
190
     *
191
     * @return void
192
     */
193
    public static function printMainSources(Fact $fact, int $level): void
194
    {
195
        $factrec = $fact->gedcom();
196
        $tree    = $fact->record()->tree();
197
198
        $nlevel = $level + 1;
199
        if ($fact->isPendingAddition()) {
200
            $styleadd = 'wt-new';
201
            $can_edit = $level === 1 && $fact->canEdit();
202
        } elseif ($fact->isPendingDeletion()) {
203
            $styleadd = 'wt-old';
204
            $can_edit = false;
205
        } else {
206
            $styleadd = '';
207
            $can_edit = $level === 1 && $fact->canEdit();
208
        }
209
210
        // -- find source for each fact
211
        preg_match_all('/(?:^|\n)(' . $level . ' SOUR (.*)(?:\n[' . $nlevel . '-9] .*)*)/', $fact->gedcom(), $matches, PREG_SET_ORDER);
212
213
        foreach ($matches as $match) {
214
            $srec   = $match[1];
215
            $sid    = $match[2];
216
            $source = Registry::sourceFactory()->make(trim($sid, '@'), $tree);
217
            // Allow access to "1 SOUR @non_existent_source@", so it can be corrected/deleted
218
            if (!$source || $source->canShow()) {
219
                if ($level > 1) {
220
                    echo '<tr class="wt-level-two-source collapse">';
221
                } else {
222
                    echo '<tr>';
223
                }
224
                echo '<th class="';
225
                if ($level > 1) {
226
                    echo 'rela ';
227
                }
228
                echo $styleadd, '">';
229
                echo $fact->label();
230
                if ($can_edit) {
231
                    echo view('fact-edit-links', ['fact' => $fact, 'url' => null]);
232
                }
233
                echo '</th>';
234
                echo '<td class="', $styleadd, '">';
235
                if ($source) {
236
                    echo '<a href="', e($source->url()), '">', $source->fullName(), '</a>';
237
                    // 2 RESN tags. Note, there can be more than one, such as "privacy" and "locked"
238
                    if (preg_match_all("/\n2 RESN (.+)/", $factrec, $rmatches)) {
239
                        $label = Registry::elementFactory()->make($fact->tag() . ':RESN')->label();
240
                        foreach ($rmatches[1] as $rmatch) {
241
                            echo '<br><span class="label">', $label, ':</span> <span class="field">';
242
                            switch ($rmatch) {
243
                                case 'none':
244
                                    // Note: "2 RESN none" is not valid gedcom, and the GUI will not let you add it.
245
                                    // However, webtrees privacy rules will interpret it as "show an otherwise private fact to public".
246
                                    echo '<i class="icon-resn-none"></i> ', I18N::translate('Show to visitors');
247
                                    break;
248
                                case 'privacy':
249
                                    echo '<i class="icon-resn-privacy"></i> ', I18N::translate('Show to members');
250
                                    break;
251
                                case 'confidential':
252
                                    echo '<i class="icon-resn-confidential"></i> ', I18N::translate('Show to managers');
253
                                    break;
254
                                case 'locked':
255
                                    echo '<i class="icon-resn-locked"></i> ', I18N::translate('Only managers can edit');
256
                                    break;
257
                                default:
258
                                    echo $rmatch;
259
                                    break;
260
                            }
261
                            echo '</span>';
262
                        }
263
                    }
264
                    echo self::printSourceStructure($tree, self::getSourceStructure($srec));
265
                    echo '<div class="indent">';
266
                    self::printMediaLinks($tree, $srec, $nlevel);
267
                    if ($nlevel === 2) {
268
                        self::printMediaLinks($tree, $source->gedcom(), 1);
269
                    }
270
                    echo FunctionsPrint::printFactNotes($tree, $srec, $nlevel);
271
                    if ($nlevel === 2) {
272
                        echo FunctionsPrint::printFactNotes($tree, $source->gedcom(), 1);
273
                    }
274
                    echo '</div>';
275
                } else {
276
                    echo $sid;
277
                }
278
                echo '</td></tr>';
279
            }
280
        }
281
    }
282
283
    /**
284
     * Print SOUR structure
285
     *  This function prints the input array of SOUR sub-records built by the
286
     *  getSourceStructure() function.
287
     *
288
     * @param Tree                        $tree
289
     * @param array<string|array<string>> $textSOUR
290
     *
291
     * @return string
292
     */
293
    public static function printSourceStructure(Tree $tree, array $textSOUR): string
294
    {
295
        $html = '';
296
297
        if ($textSOUR['PAGE'] !== '') {
298
            $html .= Registry::elementFactory()->make('INDI:SOUR:PAGE')->labelValue($textSOUR['PAGE'], $tree);
0 ignored issues
show
Bug introduced by
It seems like $textSOUR['PAGE'] can also be of type string[]; however, parameter $value of Fisharebest\Webtrees\Con...Interface::labelValue() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

298
            $html .= Registry::elementFactory()->make('INDI:SOUR:PAGE')->labelValue(/** @scrutinizer ignore-type */ $textSOUR['PAGE'], $tree);
Loading history...
299
        }
300
301
        if ($textSOUR['EVEN'] !== '') {
302
            $html .= Registry::elementFactory()->make('INDI:SOUR:EVEN')->labelValue($textSOUR['EVEN'], $tree);
303
304
            if ($textSOUR['ROLE']) {
305
                $html .= Registry::elementFactory()->make('INDI:SOUR:EVEN:ROLE')->labelValue($textSOUR['ROLE'], $tree);
306
            }
307
        }
308
309
        if ($textSOUR['DATE'] !== '') {
310
            $html .= Registry::elementFactory()->make('INDI:SOUR:DATA:DATE')->labelValue($textSOUR['DATE'], $tree);
311
        }
312
313
        foreach ($textSOUR['TEXT'] as $text) {
314
            $html .= Registry::elementFactory()->make('INDI:SOUR:DATA:TEXT')->labelValue($text, $tree);
315
        }
316
317
        if ($textSOUR['QUAY'] !== '') {
318
            $html .= Registry::elementFactory()->make('INDI:SOUR:QUAY')->labelValue($textSOUR['QUAY'], $tree);
319
        }
320
321
        return '<div class="indent">' . $html . '</div>';
322
    }
323
324
    /**
325
     * Extract SOUR structure from the incoming Source sub-record
326
     * The output array is defined as follows:
327
     *  $textSOUR['PAGE'] = Source citation
328
     *  $textSOUR['EVEN'] = Event type
329
     *  $textSOUR['ROLE'] = Role in event
330
     *  $textSOUR['DATA'] = place holder (no text in this sub-record)
331
     *  $textSOUR['DATE'] = Entry recording date
332
     *  $textSOUR['TEXT'] = (array) Text from source
333
     *  $textSOUR['QUAY'] = Certainty assessment
334
     *
335
     * @param string $srec
336
     *
337
     * @return array<array<string>>
338
     */
339
    public static function getSourceStructure(string $srec): array
340
    {
341
        // Set up the output array
342
        $textSOUR = [
343
            'PAGE' => '',
344
            'EVEN' => '',
345
            'ROLE' => '',
346
            'DATE' => '',
347
            'TEXT' => [],
348
            'QUAY' => '',
349
        ];
350
351
        preg_match_all('/^\d (PAGE|EVEN|ROLE|DATE|TEXT|QUAY) ?(.*(\n\d CONT.*)*)$/m', $srec, $matches, PREG_SET_ORDER);
352
353
        foreach ($matches as $match) {
354
            $tag   = $match[1];
355
            $value = $match[2];
356
            $value = preg_replace('/\n\d CONT ?/', "\n", $value);
357
358
            if ($tag === 'TEXT') {
359
                $textSOUR[$tag][] = $value;
360
            } else {
361
                $textSOUR[$tag] = $value;
362
            }
363
        }
364
365
        return $textSOUR;
366
    }
367
368
    /**
369
     * Print a row for the media tab on the individual page.
370
     *
371
     * @param Fact $fact
372
     * @param int  $level
373
     *
374
     * @return void
375
     */
376
    public static function printMainMedia(Fact $fact, int $level): void
377
    {
378
        $tree = $fact->record()->tree();
379
380
        if ($fact->isPendingAddition()) {
381
            $styleadd = 'wt-new';
382
        } elseif ($fact->isPendingDeletion()) {
383
            $styleadd = 'wt-old';
384
        } else {
385
            $styleadd = '';
386
        }
387
388
        // -- find source for each fact
389
        preg_match_all('/(?:^|\n)' . $level . ' OBJE @(.*)@/', $fact->gedcom(), $matches);
390
        foreach ($matches[1] as $xref) {
391
            $media = Registry::mediaFactory()->make($xref, $tree);
392
            // Allow access to "1 OBJE @non_existent_source@", so it can be corrected/deleted
393
            if (!$media instanceof Media || $media->canShow()) {
394
                echo '<tr class="', $styleadd, '">';
395
                echo '<th scope="row">';
396
                echo $fact->label();
397
398
                if ($level === 1 && $fact->canEdit()) {
399
                    echo view('fact-edit-links', ['fact' => $fact, 'url' => null]);
400
                }
401
402
                echo '</th>';
403
                echo '<td>';
404
                if ($media instanceof Media) {
405
                    foreach ($media->mediaFiles() as $media_file) {
406
                        echo '<div>';
407
                        echo $media_file->displayImage(100, 100, 'contain', []);
408
                        echo '<br>';
409
                        echo '<a href="' . e($media->url()) . '"> ';
410
                        echo '<em>';
411
                        echo e($media_file->title() ?: $media_file->filename());
412
                        echo '</em>';
413
                        echo '</a>';
414
                        echo '</div>';
415
                    }
416
417
                    echo FunctionsPrint::printFactNotes($tree, $media->gedcom(), 1);
418
                    echo self::printFactSources($tree, $media->gedcom(), 1);
419
                } else {
420
                    echo $xref;
421
                }
422
                echo '</td></tr>';
423
            }
424
        }
425
    }
426
}
427