Total Complexity | 60 |
Total Lines | 374 |
Duplicated Lines | 0 % |
Changes | 5 | ||
Bugs | 0 | Features | 0 |
Complex classes like FunctionsPrintFacts 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 FunctionsPrintFacts, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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); |
||
|
|||
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 |
||
423 | } |
||
424 | } |
||
425 | } |
||
426 | } |
||
427 |