| 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 |