| Total Complexity | 95 |
| Total Lines | 1072 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like AdminTreesController 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 AdminTreesController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 47 | class AdminTreesController extends AbstractBaseController { |
||
| 48 | // Show a reduced page when there are more than a certain number of trees |
||
| 49 | const MULTIPLE_TREE_THRESHOLD = 500; |
||
| 50 | |||
| 51 | protected $layout = 'layouts/administration'; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @param Request $request |
||
| 55 | * |
||
| 56 | * @return RedirectResponse |
||
| 57 | */ |
||
| 58 | public function create(Request $request): RedirectResponse { |
||
| 59 | /** @var Tree $tree */ |
||
| 60 | $tree = $request->attributes->get('tree'); |
||
| 61 | |||
| 62 | $tree_name = $request->get('tree_name', ''); |
||
| 63 | $tree_title = $request->get('tree_title', ''); |
||
| 64 | |||
| 65 | // We use the tree name as a file name, so no directory separators allowed. |
||
| 66 | $tree_name = basename($tree_name); |
||
| 67 | |||
| 68 | if ($tree_name !== '' && $tree_title !== '') { |
||
| 69 | if (Tree::findByName($tree_name)) { |
||
| 70 | FlashMessages::addMessage(I18N::translate('The family tree “%s” already exists.', e($tree_name)), 'danger'); |
||
| 71 | } else { |
||
| 72 | $tree = Tree::create($tree_name, $tree_title); |
||
| 73 | FlashMessages::addMessage(I18N::translate('The family tree “%s” has been created.', e($tree->getName())), 'success'); |
||
| 74 | } |
||
| 75 | } |
||
| 76 | |||
| 77 | $url = route('admin-trees', ['ged' => $tree->getName()]); |
||
| 78 | |||
| 79 | return new RedirectResponse($url); |
||
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * @param Request $request |
||
| 84 | * |
||
| 85 | * @return RedirectResponse |
||
| 86 | */ |
||
| 87 | public function delete(Request $request): RedirectResponse { |
||
| 88 | /** @var Tree $tree */ |
||
| 89 | $tree = $request->attributes->get('tree'); |
||
| 90 | |||
| 91 | FlashMessages::addMessage(/* I18N: %s is the name of a family tree */ |
||
| 92 | I18N::translate('The family tree “%s” has been deleted.', e($tree->getTitle())), 'success'); |
||
| 93 | |||
| 94 | $tree->delete(); |
||
| 95 | |||
| 96 | $url = route('admin-trees'); |
||
| 97 | |||
| 98 | return new RedirectResponse($url); |
||
| 99 | } |
||
| 100 | |||
| 101 | /** |
||
| 102 | * @param Request $request |
||
| 103 | * |
||
| 104 | * @return Response |
||
| 105 | */ |
||
| 106 | public function export(Request $request): Response { |
||
| 107 | /** @var Tree $tree */ |
||
| 108 | $tree = $request->attributes->get('tree'); |
||
| 109 | |||
| 110 | $title = I18N::translate('Export a GEDCOM file') . ' — ' . e($tree->getTitle()); |
||
| 111 | |||
| 112 | return $this->viewResponse('admin/trees-export', [ |
||
| 113 | 'title' => $title, |
||
| 114 | 'tree' => $tree, |
||
| 115 | ]); |
||
| 116 | } |
||
| 117 | |||
| 118 | /** |
||
| 119 | * @param Request $request |
||
| 120 | * |
||
| 121 | * @return Response |
||
| 122 | */ |
||
| 123 | public function exportClient(Request $request): Response { |
||
| 124 | /** @var Tree $tree */ |
||
| 125 | $tree = $request->attributes->get('tree'); |
||
| 126 | |||
| 127 | // Validate user parameters |
||
| 128 | $convert = (bool) $request->get('convert'); |
||
| 129 | $zip = (bool) $request->get('zip'); |
||
| 130 | $media = (bool) $request->get('media'); |
||
| 131 | $media_path = $request->get('media-path'); |
||
| 132 | $privatize_export = $request->get('privatize_export'); |
||
| 133 | |||
| 134 | $exportOptions = [ |
||
| 135 | 'privatize' => $privatize_export, |
||
| 136 | 'toANSI' => $convert ? 'yes' : 'no', |
||
| 137 | 'path' => $media_path, |
||
| 138 | ]; |
||
| 139 | |||
| 140 | // What to call the downloaded file |
||
| 141 | $download_filename = $tree->getName(); |
||
| 142 | if (strtolower(substr($download_filename, -4, 4)) != '.ged') { |
||
| 143 | $download_filename .= '.ged'; |
||
| 144 | } |
||
| 145 | |||
| 146 | if ($zip || $media) { |
||
| 147 | // Export the GEDCOM to an in-memory stream. |
||
| 148 | $tmp_stream = tmpfile(); |
||
| 149 | FunctionsExport::exportGedcom($tree, $tmp_stream, $exportOptions); |
||
|
|
|||
| 150 | rewind($tmp_stream); |
||
| 151 | |||
| 152 | // Create a new/empty .ZIP file |
||
| 153 | $temp_zip_file = tempnam(sys_get_temp_dir(), 'webtrees-zip-'); |
||
| 154 | $zip_filesystem = new Filesystem(new ZipArchiveAdapter($temp_zip_file)); |
||
| 155 | $zip_filesystem->writeStream($download_filename, $tmp_stream); |
||
| 156 | |||
| 157 | if ($media) { |
||
| 158 | $rows = Database::prepare( |
||
| 159 | "SELECT m_id, m_gedcom FROM `##media` WHERE m_file = :tree_id" |
||
| 160 | )->execute([ |
||
| 161 | 'tree_id' => $tree->getTreeId(), |
||
| 162 | ])->fetchAll(); |
||
| 163 | $path = $tree->getPreference('MEDIA_DIRECTORY'); |
||
| 164 | foreach ($rows as $row) { |
||
| 165 | $record = Media::getInstance($row->m_id, $tree, $row->m_gedcom); |
||
| 166 | if ($record->canShow()) { |
||
| 167 | foreach ($record->mediaFiles() as $media_file) { |
||
| 168 | if (file_exists($media_file->getServerFilename())) { |
||
| 169 | $fp = fopen($media_file->getServerFilename(), 'r'); |
||
| 170 | $zip_filesystem->writeStream($path . $media_file->filename(), $fp); |
||
| 171 | fclose($fp); |
||
| 172 | } |
||
| 173 | } |
||
| 174 | } |
||
| 175 | } |
||
| 176 | } |
||
| 177 | |||
| 178 | // The ZipArchiveAdapter may or may not close the stream. |
||
| 179 | if (is_resource($tmp_stream)) { |
||
| 180 | fclose($tmp_stream); |
||
| 181 | } |
||
| 182 | |||
| 183 | // Need to force-close the filesystem |
||
| 184 | $zip_filesystem = null; |
||
| 185 | |||
| 186 | $response = new BinaryFileResponse($temp_zip_file); |
||
| 187 | $response->deleteFileAfterSend(true); |
||
| 188 | |||
| 189 | $response->headers->set('Content-Type', 'application/zip'); |
||
| 190 | $response->setContentDisposition( |
||
| 191 | ResponseHeaderBag::DISPOSITION_ATTACHMENT, |
||
| 192 | $download_filename . '.zip' |
||
| 193 | ); |
||
| 194 | } else { |
||
| 195 | $response = new StreamedResponse(function() use ($tree, $exportOptions) { |
||
| 196 | $stream = fopen('php://output', 'w'); |
||
| 197 | FunctionsExport::exportGedcom($tree, $stream, $exportOptions); |
||
| 198 | fclose($stream); |
||
| 199 | }); |
||
| 200 | |||
| 201 | $charset = $convert ? 'ISO-8859-1' : 'UTF-8'; |
||
| 202 | |||
| 203 | $response->headers->set('Content-Type', 'text/plain; charset=' . $charset); |
||
| 204 | $contentDisposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $download_filename); |
||
| 205 | $response->headers->set('Content-Disposition', $contentDisposition); |
||
| 206 | } |
||
| 207 | |||
| 208 | return $response; |
||
| 209 | } |
||
| 210 | |||
| 211 | /** |
||
| 212 | * @param Request $request |
||
| 213 | * |
||
| 214 | * @return RedirectResponse |
||
| 215 | */ |
||
| 216 | public function exportServer(Request $request): RedirectResponse { |
||
| 217 | /** @var Tree $tree */ |
||
| 218 | $tree = $request->attributes->get('tree'); |
||
| 219 | |||
| 220 | $filename = WT_DATA_DIR . $tree->getName(); |
||
| 221 | |||
| 222 | // Force a ".ged" suffix |
||
| 223 | if (strtolower(substr($filename, -4)) != '.ged') { |
||
| 224 | $filename .= '.ged'; |
||
| 225 | } |
||
| 226 | |||
| 227 | try { |
||
| 228 | // To avoid partial trees on timeout/diskspace/etc, write to a temporary file first |
||
| 229 | $stream = fopen($filename . '.tmp', 'w'); |
||
| 230 | $tree->exportGedcom($stream); |
||
| 231 | fclose($stream); |
||
| 232 | rename($filename . '.tmp', $filename); |
||
| 233 | |||
| 234 | FlashMessages::addMessage(/* I18N: %s is a filename */ I18N::translate('The family tree has been exported to %s.', Html::filename($filename)), 'success'); |
||
| 235 | } catch (Throwable $ex) { |
||
| 236 | DebugBar::addThrowable($ex); |
||
| 237 | |||
| 238 | FlashMessages::addMessage( |
||
| 239 | I18N::translate('The file %s could not be created.', Html::filename($filename)) . '<hr><samp dir="ltr">' . $ex->getMessage() . '</samp>', |
||
| 240 | 'danger' |
||
| 241 | ); |
||
| 242 | } |
||
| 243 | |||
| 244 | $url = route('admin-trees', [ |
||
| 245 | 'ged' => $tree->getName(), |
||
| 246 | ]); |
||
| 247 | |||
| 248 | return new RedirectResponse($url); |
||
| 249 | } |
||
| 250 | |||
| 251 | |||
| 252 | /** |
||
| 253 | * @param Request $request |
||
| 254 | * |
||
| 255 | * @return RedirectResponse |
||
| 256 | */ |
||
| 257 | public function importAction(Request $request): RedirectResponse { |
||
| 258 | /** @var Tree $tree */ |
||
| 259 | $tree = $request->attributes->get('tree'); |
||
| 260 | |||
| 261 | $source = $request->get('source'); |
||
| 262 | $keep_media = (bool) $request->get('keep_media'); |
||
| 263 | $WORD_WRAPPED_NOTES = (bool) $request->get('WORD_WRAPPED_NOTES'); |
||
| 264 | $GEDCOM_MEDIA_PATH = $request->get('GEDCOM_MEDIA_PATH'); |
||
| 265 | |||
| 266 | // Save these choices as defaults |
||
| 267 | $tree->setPreference('keep_media', $keep_media ? '1' : '0'); |
||
| 268 | $tree->setPreference('WORD_WRAPPED_NOTES', $WORD_WRAPPED_NOTES ? '1' : '0'); |
||
| 269 | $tree->setPreference('GEDCOM_MEDIA_PATH', $GEDCOM_MEDIA_PATH); |
||
| 270 | |||
| 271 | if ($source === 'client') { |
||
| 272 | if (isset($_FILES['tree_name'])) { |
||
| 273 | if ($_FILES['tree_name']['error'] == 0 && is_readable($_FILES['tree_name']['tmp_name'])) { |
||
| 274 | $tree->importGedcomFile($_FILES['tree_name']['tmp_name'], $_FILES['tree_name']['name']); |
||
| 275 | } else { |
||
| 276 | FlashMessages::addMessage(Functions::fileUploadErrorText($_FILES['tree_name']['error']), 'danger'); |
||
| 277 | } |
||
| 278 | } else { |
||
| 279 | FlashMessages::addMessage(I18N::translate('No GEDCOM file was received.'), 'danger'); |
||
| 280 | } |
||
| 281 | } |
||
| 282 | |||
| 283 | if ($source === 'server') { |
||
| 284 | $basename = basename($request->get('tree_name')); |
||
| 285 | |||
| 286 | if ($basename) { |
||
| 287 | $tree->importGedcomFile(WT_DATA_DIR . $basename, $basename); |
||
| 288 | } else { |
||
| 289 | FlashMessages::addMessage(I18N::translate('No GEDCOM file was received.'), 'danger'); |
||
| 290 | } |
||
| 291 | } |
||
| 292 | |||
| 293 | $url = route('admin-trees', ['ged' => $tree->getName()]); |
||
| 294 | |||
| 295 | return new RedirectResponse($url); |
||
| 296 | } |
||
| 297 | |||
| 298 | /** |
||
| 299 | * @param Request $request |
||
| 300 | * |
||
| 301 | * @return Response |
||
| 302 | */ |
||
| 303 | public function importForm(Request $request): Response { |
||
| 304 | /** @var Tree $tree */ |
||
| 305 | $tree = $request->attributes->get('tree'); |
||
| 306 | |||
| 307 | $default_gedcom_file = $tree->getPreference('gedcom_filename'); |
||
| 308 | $gedcom_media_path = $tree->getPreference('GEDCOM_MEDIA_PATH'); |
||
| 309 | $gedcom_files = $this->gedcomFiles(WT_DATA_DIR); |
||
| 310 | |||
| 311 | $title = I18N::translate('Import a GEDCOM file') . ' — ' . e($tree->getTitle()); |
||
| 312 | |||
| 313 | return $this->viewResponse('admin/tree-import', [ |
||
| 314 | 'default_gedcom_file' => $default_gedcom_file, |
||
| 315 | 'gedcom_files' => $gedcom_files, |
||
| 316 | 'gedcom_media_path' => $gedcom_media_path, |
||
| 317 | 'title' => $title, |
||
| 318 | ]); |
||
| 319 | } |
||
| 320 | |||
| 321 | /** |
||
| 322 | * @param Request $request |
||
| 323 | * |
||
| 324 | * @return Response |
||
| 325 | */ |
||
| 326 | public function index(Request $request): Response { |
||
| 327 | /** @var Tree $tree */ |
||
| 328 | $tree = $request->attributes->get('tree'); |
||
| 329 | |||
| 330 | $multiple_tree_threshold = (int) Site::getPreference('MULTIPLE_TREE_THRESHOLD', self::MULTIPLE_TREE_THRESHOLD); |
||
| 331 | $gedcom_files = $this->gedcomFiles(WT_DATA_DIR); |
||
| 332 | |||
| 333 | $all_trees = Tree::getAll(); |
||
| 334 | |||
| 335 | // On sites with hundreds or thousands of trees, this page becomes very large. |
||
| 336 | // Just show the current tree, the default tree, and unimported trees |
||
| 337 | if (count($all_trees) >= $multiple_tree_threshold) { |
||
| 338 | $all_trees = array_filter($all_trees, function (Tree $x) use ($tree) { |
||
| 339 | return $x->getPreference('imported') === '0' || $tree->getTreeId() === $x->getTreeId() || $x->getName() === Site::getPreference('DEFAULT_GEDCOM'); |
||
| 340 | }); |
||
| 341 | } |
||
| 342 | |||
| 343 | $default_tree_name = $this->generateNewTreeName(); |
||
| 344 | $default_tree_title = I18N::translate('My family tree'); |
||
| 345 | |||
| 346 | $all_users = User::all(); |
||
| 347 | |||
| 348 | $title = I18N::translate('Manage family trees'); |
||
| 349 | |||
| 350 | return $this->viewResponse('admin/trees', [ |
||
| 351 | 'all_trees' => $all_trees, |
||
| 352 | 'all_users' => $all_users, |
||
| 353 | 'default_tree_name' => $default_tree_name, |
||
| 354 | 'default_tree_title' => $default_tree_title, |
||
| 355 | 'gedcom_files' => $gedcom_files, |
||
| 356 | 'multiple_tree_threshold' => $multiple_tree_threshold, |
||
| 357 | 'title' => $title, |
||
| 358 | ]); |
||
| 359 | } |
||
| 360 | |||
| 361 | /** |
||
| 362 | * @param Request $request |
||
| 363 | * |
||
| 364 | * @return Response |
||
| 365 | */ |
||
| 366 | public function merge(Request $request): Response { |
||
| 367 | $tree1_name = $request->get('tree1_name'); |
||
| 368 | $tree2_name = $request->get('tree2_name'); |
||
| 369 | |||
| 370 | $tree1 = Tree::findByName($tree1_name); |
||
| 371 | $tree2 = Tree::findByName($tree2_name); |
||
| 372 | |||
| 373 | if ($tree1 !== null && $tree2 !== null && $tree1->getTreeId() !== $tree2->getTreeId()) { |
||
| 374 | $xrefs = $this->commonXrefs($tree1, $tree2); |
||
| 375 | } else { |
||
| 376 | $xrefs = []; |
||
| 377 | } |
||
| 378 | |||
| 379 | $tree_list = Tree::getNameList(); |
||
| 380 | |||
| 381 | $title = I18N::translate(I18N::translate('Merge family trees')); |
||
| 382 | |||
| 383 | return $this->viewResponse('admin/trees-merge', [ |
||
| 384 | 'tree_list' => $tree_list, |
||
| 385 | 'tree1' => $tree1, |
||
| 386 | 'tree2' => $tree2, |
||
| 387 | 'title' => $title, |
||
| 388 | 'xrefs' => $xrefs, |
||
| 389 | ]); |
||
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * @param Request $request |
||
| 394 | * |
||
| 395 | * @return RedirectResponse |
||
| 396 | */ |
||
| 397 | public function mergeAction(Request $request): RedirectResponse { |
||
| 485 | } |
||
| 486 | |||
| 487 | /** |
||
| 488 | * @param Request $request |
||
| 489 | * |
||
| 490 | * @return Response |
||
| 491 | */ |
||
| 492 | public function places(Request $request): Response { |
||
| 493 | /** @var Tree $tree */ |
||
| 494 | $tree = $request->attributes->get('tree'); |
||
| 495 | |||
| 496 | $search = $request->get('search', ''); |
||
| 497 | $replace = $request->get('replace', ''); |
||
| 498 | |||
| 499 | if ($search !== '' && $replace !== '') { |
||
| 500 | $changes = $this->changePlacesPreview($tree, $search, $replace); |
||
| 501 | } else { |
||
| 502 | $changes = []; |
||
| 503 | } |
||
| 504 | |||
| 505 | $title = I18N::translate(/* I18N: Renumber the records in a family tree */ |
||
| 506 | 'Renumber family tree') . ' — ' . e($tree->getTitle()); |
||
| 507 | |||
| 508 | return $this->viewResponse('admin/trees-places', [ |
||
| 509 | 'changes' => $changes, |
||
| 510 | 'replace' => $replace, |
||
| 511 | 'search' => $search, |
||
| 512 | 'title' => $title, |
||
| 513 | ]); |
||
| 514 | } |
||
| 515 | |||
| 516 | /** |
||
| 517 | * @param Request $request |
||
| 518 | * |
||
| 519 | * @return RedirectResponse |
||
| 520 | */ |
||
| 521 | public function placesAction(Request $request): RedirectResponse { |
||
| 522 | /** @var Tree $tree */ |
||
| 523 | $tree = $request->attributes->get('tree'); |
||
| 524 | |||
| 525 | $search = $request->get('search', ''); |
||
| 526 | $replace = $request->get('replace', ''); |
||
| 527 | |||
| 528 | $changes = $this->changePlacesUpdate($tree, $search, $replace); |
||
| 529 | |||
| 530 | $feedback = I18N::translate('The following places have been changed:') . '<ul>'; |
||
| 531 | foreach ($changes as $old_place => $new_place) { |
||
| 532 | $feedback .= '<li>' . e($old_place) . ' → ' . e($new_place) . '</li>'; |
||
| 533 | } |
||
| 534 | $feedback .= '</ul>'; |
||
| 535 | |||
| 536 | FlashMessages::addMessage($feedback, 'success'); |
||
| 537 | |||
| 538 | $url = route('admin-trees-places', [ |
||
| 539 | 'ged' => $tree->getName(), |
||
| 540 | 'replace' => $replace, |
||
| 541 | 'search' => $search, |
||
| 542 | ]); |
||
| 543 | |||
| 544 | return new RedirectResponse($url); |
||
| 545 | } |
||
| 546 | |||
| 547 | /** |
||
| 548 | * @param Request $request |
||
| 549 | * |
||
| 550 | * @return Response |
||
| 551 | */ |
||
| 552 | public function renumber(Request $request): Response { |
||
| 553 | /** @var Tree $tree */ |
||
| 554 | $tree = $request->attributes->get('tree'); |
||
| 555 | |||
| 556 | $xrefs = $this->duplicateXrefs($tree); |
||
| 557 | |||
| 558 | $title = I18N::translate(/* I18N: Renumber the records in a family tree */ |
||
| 559 | 'Renumber family tree') . ' — ' . e($tree->getTitle()); |
||
| 560 | |||
| 561 | return $this->viewResponse('admin/trees-renumber', [ |
||
| 562 | 'title' => $title, |
||
| 563 | 'xrefs' => $xrefs, |
||
| 564 | ]); |
||
| 565 | } |
||
| 566 | |||
| 567 | /** |
||
| 568 | * @param Request $request |
||
| 569 | * |
||
| 570 | * @return RedirectResponse |
||
| 571 | */ |
||
| 572 | public function renumberAction(Request $request): RedirectResponse { |
||
| 573 | /** @var Tree $tree */ |
||
| 574 | $tree = $request->attributes->get('tree'); |
||
| 575 | |||
| 576 | $xrefs = $this->duplicateXrefs($tree); |
||
| 577 | |||
| 578 | foreach ($xrefs as $old_xref => $type) { |
||
| 579 | $new_xref = $tree->getNewXref(); |
||
| 580 | switch ($type) { |
||
| 581 | case 'INDI': |
||
| 582 | Database::prepare( |
||
| 583 | "UPDATE `##individuals` SET i_id = ?, i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_id = ? AND i_file = ?" |
||
| 584 | )->execute([$new_xref, "0 @$old_xref@ INDI\n", "0 @$new_xref@ INDI\n", $old_xref, $tree->getTreeId()]); |
||
| 585 | Database::prepare( |
||
| 586 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'HUSB') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 587 | )->execute([$old_xref, " HUSB @$old_xref@", " HUSB @$new_xref@", $tree->getTreeId()]); |
||
| 588 | Database::prepare( |
||
| 589 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'WIFE') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 590 | )->execute([$old_xref, " WIFE @$old_xref@", " WIFE @$new_xref@", $tree->getTreeId()]); |
||
| 591 | Database::prepare( |
||
| 592 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'CHIL') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 593 | )->execute([$old_xref, " CHIL @$old_xref@", " CHIL @$new_xref@", $tree->getTreeId()]); |
||
| 594 | Database::prepare( |
||
| 595 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'ASSO') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 596 | )->execute([$old_xref, " ASSO @$old_xref@", " ASSO @$new_xref@", $tree->getTreeId()]); |
||
| 597 | Database::prepare( |
||
| 598 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = '_ASSO') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 599 | )->execute([$old_xref, " _ASSO @$old_xref@", " _ASSO @$new_xref@", $tree->getTreeId()]); |
||
| 600 | Database::prepare( |
||
| 601 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'ASSO') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 602 | )->execute([$old_xref, " ASSO @$old_xref@", " ASSO @$new_xref@", $tree->getTreeId()]); |
||
| 603 | Database::prepare( |
||
| 604 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = '_ASSO') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 605 | )->execute([$old_xref, " _ASSO @$old_xref@", " _ASSO @$new_xref@", $tree->getTreeId()]); |
||
| 606 | Database::prepare( |
||
| 607 | "UPDATE `##placelinks` SET pl_gid = ? WHERE pl_gid = ? AND pl_file = ?" |
||
| 608 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 609 | Database::prepare( |
||
| 610 | "UPDATE `##dates` SET d_gid = ? WHERE d_gid = ? AND d_file = ?" |
||
| 611 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 612 | Database::prepare( |
||
| 613 | "UPDATE `##user_gedcom_setting` SET setting_value = ? WHERE setting_value = ? AND gedcom_id = ? AND setting_name IN ('gedcomid', 'rootid')" |
||
| 614 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 615 | break; |
||
| 616 | case 'FAM': |
||
| 617 | Database::prepare( |
||
| 618 | "UPDATE `##families` SET f_id = ?, f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_id = ? AND f_file = ?" |
||
| 619 | )->execute([$new_xref, "0 @$old_xref@ FAM\n", "0 @$new_xref@ FAM\n", $old_xref, $tree->getTreeId()]); |
||
| 620 | Database::prepare( |
||
| 621 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'FAMC') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 622 | )->execute([$old_xref, " FAMC @$old_xref@", " FAMC @$new_xref@", $tree->getTreeId()]); |
||
| 623 | Database::prepare( |
||
| 624 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'FAMS') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 625 | )->execute([$old_xref, " FAMS @$old_xref@", " FAMS @$new_xref@", $tree->getTreeId()]); |
||
| 626 | Database::prepare( |
||
| 627 | "UPDATE `##placelinks` SET pl_gid = ? WHERE pl_gid = ? AND pl_file = ?" |
||
| 628 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 629 | Database::prepare( |
||
| 630 | "UPDATE `##dates` SET d_gid = ? WHERE d_gid = ? AND d_file = ?" |
||
| 631 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 632 | break; |
||
| 633 | case 'SOUR': |
||
| 634 | Database::prepare( |
||
| 635 | "UPDATE `##sources` SET s_id = ?, s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_id = ? AND s_file = ?" |
||
| 636 | )->execute([$new_xref, "0 @$old_xref@ SOUR\n", "0 @$new_xref@ SOUR\n", $old_xref, $tree->getTreeId()]); |
||
| 637 | Database::prepare( |
||
| 638 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'SOUR') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 639 | )->execute([$old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $tree->getTreeId()]); |
||
| 640 | Database::prepare( |
||
| 641 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'SOUR') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 642 | )->execute([$old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $tree->getTreeId()]); |
||
| 643 | Database::prepare( |
||
| 644 | "UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ? AND l_type = 'SOUR') SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?" |
||
| 645 | )->execute([$old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $tree->getTreeId()]); |
||
| 646 | Database::prepare( |
||
| 647 | "UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ? AND l_type = 'SOUR') SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?" |
||
| 648 | )->execute([$old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $tree->getTreeId()]); |
||
| 649 | break; |
||
| 650 | case 'REPO': |
||
| 651 | Database::prepare( |
||
| 652 | "UPDATE `##other` SET o_id = ?, o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_id = ? AND o_file = ?" |
||
| 653 | )->execute([$new_xref, "0 @$old_xref@ REPO\n", "0 @$new_xref@ REPO\n", $old_xref, $tree->getTreeId()]); |
||
| 654 | Database::prepare( |
||
| 655 | "UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ? AND l_type = 'REPO') SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?" |
||
| 656 | )->execute([$old_xref, " REPO @$old_xref@", " REPO @$new_xref@", $tree->getTreeId()]); |
||
| 657 | break; |
||
| 658 | case 'NOTE': |
||
| 659 | Database::prepare( |
||
| 660 | "UPDATE `##other` SET o_id = ?, o_gedcom = REPLACE(REPLACE(o_gedcom, ?, ?), ?, ?) WHERE o_id = ? AND o_file = ?" |
||
| 661 | )->execute([$new_xref, "0 @$old_xref@ NOTE\n", "0 @$new_xref@ NOTE\n", "0 @$old_xref@ NOTE ", "0 @$new_xref@ NOTE ", $old_xref, $tree->getTreeId()]); |
||
| 662 | Database::prepare( |
||
| 663 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'NOTE') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 664 | )->execute([$old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $tree->getTreeId()]); |
||
| 665 | Database::prepare( |
||
| 666 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'NOTE') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 667 | )->execute([$old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $tree->getTreeId()]); |
||
| 668 | Database::prepare( |
||
| 669 | "UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ? AND l_type = 'NOTE') SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?" |
||
| 670 | )->execute([$old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $tree->getTreeId()]); |
||
| 671 | Database::prepare( |
||
| 672 | "UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ? AND l_type = 'NOTE') SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?" |
||
| 673 | )->execute([$old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $tree->getTreeId()]); |
||
| 674 | Database::prepare( |
||
| 675 | "UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ? AND l_type = 'NOTE') SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?" |
||
| 676 | )->execute([$old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $tree->getTreeId()]); |
||
| 677 | break; |
||
| 678 | case 'OBJE': |
||
| 679 | Database::prepare( |
||
| 680 | "UPDATE `##media` SET m_id = ?, m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_id = ? AND m_file = ?" |
||
| 681 | )->execute([$new_xref, "0 @$old_xref@ OBJE\n", "0 @$new_xref@ OBJE\n", $old_xref, $tree->getTreeId()]); |
||
| 682 | Database::prepare( |
||
| 683 | "UPDATE `##media_file` SET m_id = ? WHERE m_id = ? AND m_file = ?" |
||
| 684 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 685 | Database::prepare( |
||
| 686 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'OBJE') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 687 | )->execute([$old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $tree->getTreeId()]); |
||
| 688 | Database::prepare( |
||
| 689 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'OBJE') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 690 | )->execute([$old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $tree->getTreeId()]); |
||
| 691 | Database::prepare( |
||
| 692 | "UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ? AND l_type = 'OBJE') SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?" |
||
| 693 | )->execute([$old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $tree->getTreeId()]); |
||
| 694 | Database::prepare( |
||
| 695 | "UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ? AND l_type = 'OBJE') SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?" |
||
| 696 | )->execute([$old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $tree->getTreeId()]); |
||
| 697 | Database::prepare( |
||
| 698 | "UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ? AND l_type = 'OBJE') SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?" |
||
| 699 | )->execute([$old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $tree->getTreeId()]); |
||
| 700 | break; |
||
| 701 | default: |
||
| 702 | Database::prepare( |
||
| 703 | "UPDATE `##other` SET o_id = ?, o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_id = ? AND o_file = ?" |
||
| 704 | )->execute([$new_xref, "0 @$old_xref@ $type\n", "0 @$new_xref@ $type\n", $old_xref, $tree->getTreeId()]); |
||
| 705 | Database::prepare( |
||
| 706 | "UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ?) SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?" |
||
| 707 | )->execute([$old_xref, " @$old_xref@", " @$new_xref@", $tree->getTreeId()]); |
||
| 708 | Database::prepare( |
||
| 709 | "UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ?) SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?" |
||
| 710 | )->execute([$old_xref, " @$old_xref@", " @$new_xref@", $tree->getTreeId()]); |
||
| 711 | Database::prepare( |
||
| 712 | "UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ?) SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?" |
||
| 713 | )->execute([$old_xref, " @$old_xref@", " @$new_xref@", $tree->getTreeId()]); |
||
| 714 | Database::prepare( |
||
| 715 | "UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ?) SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?" |
||
| 716 | )->execute([$old_xref, " @$old_xref@", " @$new_xref@", $tree->getTreeId()]); |
||
| 717 | Database::prepare( |
||
| 718 | "UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ?) SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?" |
||
| 719 | )->execute([$old_xref, " @$old_xref@", " @$new_xref@", $tree->getTreeId()]); |
||
| 720 | break; |
||
| 721 | } |
||
| 722 | Database::prepare( |
||
| 723 | "UPDATE `##name` SET n_id = ? WHERE n_id = ? AND n_file = ?" |
||
| 724 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 725 | Database::prepare( |
||
| 726 | "UPDATE `##default_resn` SET xref = ? WHERE xref = ? AND gedcom_id = ?" |
||
| 727 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 728 | Database::prepare( |
||
| 729 | "UPDATE `##hit_counter` SET page_parameter = ? WHERE page_parameter = ? AND gedcom_id = ?" |
||
| 730 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 731 | Database::prepare( |
||
| 732 | "UPDATE `##link` SET l_from = ? WHERE l_from = ? AND l_file = ?" |
||
| 733 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 734 | Database::prepare( |
||
| 735 | "UPDATE `##link` SET l_to = ? WHERE l_to = ? AND l_file = ?" |
||
| 736 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 737 | |||
| 738 | unset($xrefs[$old_xref]); |
||
| 739 | |||
| 740 | try { |
||
| 741 | Database::prepare( |
||
| 742 | "UPDATE `##favorite` SET xref = ? WHERE xref = ? AND gedcom_id = ?" |
||
| 743 | )->execute([$new_xref, $old_xref, $tree->getTreeId()]); |
||
| 744 | } catch (\Exception $ex) { |
||
| 745 | DebugBar::addThrowable($ex); |
||
| 746 | |||
| 747 | // Perhaps the favorites module was not installed? |
||
| 748 | } |
||
| 749 | |||
| 750 | // How much time do we have left? |
||
| 751 | if (microtime(true) - WT_START_TIME > ini_get('max_execution_time') - 5) { |
||
| 752 | FlashMessages::addMessage(I18N::translate('The server’s time limit has been reached.'), 'warning'); |
||
| 753 | break; |
||
| 754 | } |
||
| 755 | } |
||
| 756 | |||
| 757 | $url = route('admin-trees-renumber', ['ged' => $tree->getName()]); |
||
| 758 | |||
| 759 | return new RedirectResponse($url); |
||
| 760 | } |
||
| 761 | |||
| 762 | /** |
||
| 763 | * @param Request $request |
||
| 764 | * |
||
| 765 | * @return RedirectResponse |
||
| 766 | */ |
||
| 767 | public function setDefault(Request $request): RedirectResponse { |
||
| 768 | /** @var Tree $tree */ |
||
| 769 | $tree = $request->attributes->get('tree'); |
||
| 770 | |||
| 771 | Site::setPreference('DEFAULT_GEDCOM', $tree->getName()); |
||
| 772 | |||
| 773 | FlashMessages::addMessage(/* I18N: %s is the name of a family tree */ |
||
| 774 | I18N::translate('The family tree “%s” will be shown to visitors when they first arrive at this website.', e($tree->getTitle())), 'success'); |
||
| 775 | |||
| 776 | $url = route('admin-trees'); |
||
| 777 | |||
| 778 | return new RedirectResponse($url); |
||
| 779 | } |
||
| 780 | |||
| 781 | /** |
||
| 782 | * @param Request $request |
||
| 783 | * |
||
| 784 | * @return RedirectResponse |
||
| 785 | */ |
||
| 786 | public function synchronize(Request $request): RedirectResponse { |
||
| 787 | /** @var Tree $tree */ |
||
| 788 | $tree = $request->attributes->get('tree'); |
||
| 789 | |||
| 790 | $gedcom_files = $this->gedcomFiles(WT_DATA_DIR); |
||
| 791 | |||
| 792 | foreach ($gedcom_files as $gedcom_file) { |
||
| 793 | // Only import files that have changed |
||
| 794 | $filemtime = (string) filemtime(WT_DATA_DIR . $gedcom_file); |
||
| 795 | |||
| 796 | $tree = Tree::findByName($gedcom_file) ?? Tree::create($gedcom_file, $gedcom_file); |
||
| 797 | |||
| 798 | if ($tree->getPreference('filemtime') !== $filemtime) { |
||
| 799 | $tree->importGedcomFile(WT_DATA_DIR . $gedcom_file, $gedcom_file); |
||
| 800 | $tree->setPreference('filemtime', $filemtime); |
||
| 801 | |||
| 802 | FlashMessages::addMessage(I18N::translate('The GEDCOM file “%s” has been imported.', e($gedcom_file)), 'success'); |
||
| 803 | } |
||
| 804 | } |
||
| 805 | |||
| 806 | foreach (Tree::getAll() as $tree) { |
||
| 807 | if (!in_array($tree->getName(), $gedcom_files)) { |
||
| 808 | FlashMessages::addMessage(I18N::translate('The family tree “%s” has been deleted.', e($tree->getTitle())), 'success'); |
||
| 809 | $tree->delete(); |
||
| 810 | } |
||
| 811 | } |
||
| 812 | |||
| 813 | $url = route('admin-trees', ['ged' => $tree->getName()]); |
||
| 814 | |||
| 815 | return new RedirectResponse($url); |
||
| 816 | } |
||
| 817 | |||
| 818 | /** |
||
| 819 | * @param Request $request |
||
| 820 | * |
||
| 821 | * @return Response |
||
| 822 | */ |
||
| 823 | public function unconnected(Request $request): Response { |
||
| 874 | ]); |
||
| 875 | } |
||
| 876 | |||
| 877 | /** |
||
| 878 | * Find a list of place names that would be updated. |
||
| 879 | * |
||
| 880 | * @param Tree $tree |
||
| 881 | * @param string $search |
||
| 882 | * @param string $replace |
||
| 883 | * |
||
| 884 | * @return string[] |
||
| 885 | */ |
||
| 886 | private function changePlacesPreview(Tree $tree, string $search, string $replace): array { |
||
| 887 | $changes = []; |
||
| 888 | |||
| 889 | $rows = Database::prepare( |
||
| 890 | "SELECT i_id AS xref, COALESCE(new_gedcom, i_gedcom) AS gedcom" . |
||
| 891 | " FROM `##individuals`" . |
||
| 892 | " LEFT JOIN `##change` ON (i_id = xref AND i_file=gedcom_id AND status='pending')" . |
||
| 893 | " WHERE i_file = ?" . |
||
| 894 | " AND COALESCE(new_gedcom, i_gedcom) REGEXP CONCAT('\n2 PLAC ([^\n]*, )*', ?, '(\n|$)')" |
||
| 895 | )->execute([$tree->getTreeId(), preg_quote($search)])->fetchAll(); |
||
| 896 | foreach ($rows as $row) { |
||
| 897 | $record = Individual::getInstance($row->xref, $tree, $row->gedcom); |
||
| 898 | foreach ($record->getFacts() as $fact) { |
||
| 899 | $old_place = $fact->getAttribute('PLAC'); |
||
| 900 | if (preg_match('/(^|, )' . preg_quote($search, '/') . '$/i', $old_place)) { |
||
| 901 | $new_place = preg_replace('/(^|, )' . preg_quote($search, '/') . '$/i', '$1' . $replace, $old_place); |
||
| 902 | $changes[$old_place] = $new_place; |
||
| 903 | } |
||
| 904 | } |
||
| 905 | } |
||
| 906 | $rows = Database::prepare( |
||
| 907 | "SELECT f_id AS xref, COALESCE(new_gedcom, f_gedcom) AS gedcom" . |
||
| 908 | " FROM `##families`" . |
||
| 909 | " LEFT JOIN `##change` ON (f_id = xref AND f_file=gedcom_id AND status='pending')" . |
||
| 910 | " WHERE f_file = ?" . |
||
| 911 | " AND COALESCE(new_gedcom, f_gedcom) REGEXP CONCAT('\n2 PLAC ([^\n]*, )*', ?, '(\n|$)')" |
||
| 912 | )->execute([$tree->getTreeId(), preg_quote($search)])->fetchAll(); |
||
| 913 | foreach ($rows as $row) { |
||
| 914 | $record = Family::getInstance($row->xref, $tree, $row->gedcom); |
||
| 915 | foreach ($record->getFacts() as $fact) { |
||
| 916 | $old_place = $fact->getAttribute('PLAC'); |
||
| 917 | if (preg_match('/(^|, )' . preg_quote($search, '/') . '$/i', $old_place)) { |
||
| 918 | $new_place = preg_replace('/(^|, )' . preg_quote($search, '/') . '$/i', '$1' . $replace, $old_place); |
||
| 919 | $changes[$old_place] = $new_place; |
||
| 920 | } |
||
| 921 | } |
||
| 922 | } |
||
| 923 | |||
| 924 | asort($changes); |
||
| 925 | |||
| 926 | return $changes; |
||
| 927 | } |
||
| 928 | |||
| 929 | /** |
||
| 930 | * Find a list of place names that would be updated. |
||
| 931 | * |
||
| 932 | * @param Tree $tree |
||
| 933 | * @param string $search |
||
| 934 | * @param string $replace |
||
| 935 | * |
||
| 936 | * @return string[] |
||
| 937 | */ |
||
| 938 | private function changePlacesUpdate(Tree $tree, string $search, string $replace): array { |
||
| 939 | $changes = []; |
||
| 940 | |||
| 941 | $rows = Database::prepare( |
||
| 942 | "SELECT i_id AS xref, COALESCE(new_gedcom, i_gedcom) AS gedcom" . |
||
| 943 | " FROM `##individuals`" . |
||
| 944 | " LEFT JOIN `##change` ON (i_id = xref AND i_file=gedcom_id AND status='pending')" . |
||
| 945 | " WHERE i_file = ?" . |
||
| 946 | " AND COALESCE(new_gedcom, i_gedcom) REGEXP CONCAT('\n2 PLAC ([^\n]*, )*', ?, '(\n|$)')" |
||
| 947 | )->execute([$tree->getTreeId(), preg_quote($search)])->fetchAll(); |
||
| 948 | foreach ($rows as $row) { |
||
| 949 | $record = Individual::getInstance($row->xref, $tree, $row->gedcom); |
||
| 950 | foreach ($record->getFacts() as $fact) { |
||
| 951 | $old_place = $fact->getAttribute('PLAC'); |
||
| 952 | if (preg_match('/(^|, )' . preg_quote($search, '/') . '$/i', $old_place)) { |
||
| 953 | $new_place = preg_replace('/(^|, )' . preg_quote($search, '/') . '$/i', '$1' . $replace, $old_place); |
||
| 954 | $changes[$old_place] = $new_place; |
||
| 955 | $gedcom = preg_replace('/(\n2 PLAC (?:.*, )*)' . preg_quote($search, '/') . '(\n|$)/i', '$1' . $replace . '$2', $fact->getGedcom()); |
||
| 956 | $record->updateFact($fact->getFactId(), $gedcom, false); |
||
| 957 | } |
||
| 958 | } |
||
| 959 | } |
||
| 960 | $rows = Database::prepare( |
||
| 961 | "SELECT f_id AS xref, COALESCE(new_gedcom, f_gedcom) AS gedcom" . |
||
| 962 | " FROM `##families`" . |
||
| 963 | " LEFT JOIN `##change` ON (f_id = xref AND f_file=gedcom_id AND status='pending')" . |
||
| 964 | " WHERE f_file = ?" . |
||
| 965 | " AND COALESCE(new_gedcom, f_gedcom) REGEXP CONCAT('\n2 PLAC ([^\n]*, )*', ?, '(\n|$)')" |
||
| 966 | )->execute([$tree->getTreeId(), preg_quote($search)])->fetchAll(); |
||
| 967 | foreach ($rows as $row) { |
||
| 968 | $record = Family::getInstance($row->xref, $tree, $row->gedcom); |
||
| 969 | foreach ($record->getFacts() as $fact) { |
||
| 970 | $old_place = $fact->getAttribute('PLAC'); |
||
| 971 | if (preg_match('/(^|, )' . preg_quote($search, '/') . '$/i', $old_place)) { |
||
| 972 | $new_place = preg_replace('/(^|, )' . preg_quote($search, '/') . '$/i', '$1' . $replace, $old_place); |
||
| 973 | $changes[$old_place] = $new_place; |
||
| 974 | $gedcom = preg_replace('/(\n2 PLAC (?:.*, )*)' . preg_quote($search, '/') . '(\n|$)/i', '$1' . $replace . '$2', $fact->getGedcom()); |
||
| 975 | $record->updateFact($fact->getFactId(), $gedcom, false); |
||
| 976 | } |
||
| 977 | } |
||
| 978 | } |
||
| 979 | |||
| 980 | asort($changes); |
||
| 981 | |||
| 982 | return $changes; |
||
| 983 | } |
||
| 984 | |||
| 985 | /** |
||
| 986 | * Every XREF used by two trees at the same time. |
||
| 987 | * |
||
| 988 | * @param Tree $tree |
||
| 989 | * |
||
| 990 | * @return string[] |
||
| 991 | */ |
||
| 992 | private function commonXrefs(Tree $tree1, Tree $tree2): array { |
||
| 993 | return Database::prepare( |
||
| 994 | "SELECT xref, type FROM (" . |
||
| 995 | " SELECT i_id AS xref, 'INDI' AS type FROM `##individuals` WHERE i_file = ?" . |
||
| 996 | " UNION " . |
||
| 997 | " SELECT f_id AS xref, 'FAM' AS type FROM `##families` WHERE f_file = ?" . |
||
| 998 | " UNION " . |
||
| 999 | " SELECT s_id AS xref, 'SOUR' AS type FROM `##sources` WHERE s_file = ?" . |
||
| 1000 | " UNION " . |
||
| 1001 | " SELECT m_id AS xref, 'OBJE' AS type FROM `##media` WHERE m_file = ?" . |
||
| 1002 | " UNION " . |
||
| 1003 | " SELECT o_id AS xref, o_type AS type FROM `##other` WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')" . |
||
| 1004 | ") AS this_tree JOIN (" . |
||
| 1005 | " SELECT xref FROM `##change` WHERE gedcom_id = ?" . |
||
| 1006 | " UNION " . |
||
| 1007 | " SELECT i_id AS xref FROM `##individuals` WHERE i_file = ?" . |
||
| 1008 | " UNION " . |
||
| 1009 | " SELECT f_id AS xref FROM `##families` WHERE f_file = ?" . |
||
| 1010 | " UNION " . |
||
| 1011 | " SELECT s_id AS xref FROM `##sources` WHERE s_file = ?" . |
||
| 1012 | " UNION " . |
||
| 1013 | " SELECT m_id AS xref FROM `##media` WHERE m_file = ?" . |
||
| 1014 | " UNION " . |
||
| 1015 | " SELECT o_id AS xref FROM `##other` WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')" . |
||
| 1016 | ") AS other_trees USING (xref)" |
||
| 1017 | )->execute([ |
||
| 1018 | $tree1->getTreeId(), |
||
| 1019 | $tree1->getTreeId(), |
||
| 1020 | $tree1->getTreeId(), |
||
| 1021 | $tree1->getTreeId(), |
||
| 1022 | $tree1->getTreeId(), |
||
| 1023 | $tree2->getTreeId(), |
||
| 1024 | $tree2->getTreeId(), |
||
| 1025 | $tree2->getTreeId(), |
||
| 1026 | $tree2->getTreeId(), |
||
| 1027 | $tree2->getTreeId(), |
||
| 1028 | $tree2->getTreeId(), |
||
| 1029 | ])->fetchAssoc(); |
||
| 1030 | } |
||
| 1031 | |||
| 1032 | /** |
||
| 1033 | * Every XREF used by this tree and also used by some other tree |
||
| 1034 | * |
||
| 1035 | * @param Tree $tree |
||
| 1036 | * |
||
| 1037 | * @return string[] |
||
| 1038 | */ |
||
| 1039 | private function duplicateXrefs(Tree $tree): array { |
||
| 1077 | } |
||
| 1078 | |||
| 1079 | /** |
||
| 1080 | * Find a list of GEDCOM files in a folder |
||
| 1081 | * |
||
| 1082 | * @param string $folder |
||
| 1083 | * |
||
| 1084 | * @return array |
||
| 1085 | */ |
||
| 1086 | private function gedcomFiles(string $folder): array { |
||
| 1087 | $d = opendir($folder); |
||
| 1088 | $files = []; |
||
| 1089 | while (($f = readdir($d)) !== false) { |
||
| 1090 | if (!is_dir(WT_DATA_DIR . $f) && is_readable(WT_DATA_DIR . $f)) { |
||
| 1091 | $fp = fopen(WT_DATA_DIR . $f, 'rb'); |
||
| 1092 | $header = fread($fp, 64); |
||
| 1093 | fclose($fp); |
||
| 1094 | if (preg_match('/^(' . WT_UTF8_BOM . ')?0 *HEAD/', $header)) { |
||
| 1095 | $files[] = $f; |
||
| 1096 | } |
||
| 1097 | } |
||
| 1098 | } |
||
| 1099 | sort($files); |
||
| 1100 | |||
| 1101 | return $files; |
||
| 1102 | } |
||
| 1103 | |||
| 1104 | /** |
||
| 1105 | * Generate a unqiue name for new trees |
||
| 1106 | * |
||
| 1107 | * @return string |
||
| 1108 | */ |
||
| 1109 | private function generateNewTreeName(): string { |
||
| 1119 | } |
||
| 1120 | } |
||
| 1121 |