Total Complexity | 100 |
Total Lines | 968 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like ClippingsCartModule 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 ClippingsCartModule, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
78 | class ClippingsCartModule extends AbstractModule implements ModuleMenuInterface |
||
79 | { |
||
80 | use ModuleMenuTrait; |
||
81 | |||
82 | // Routes that have a record which can be added to the clipboard |
||
83 | private const ROUTES_WITH_RECORDS = [ |
||
84 | 'Family' => FamilyPage::class, |
||
85 | 'Individual' => IndividualPage::class, |
||
86 | 'Media' => MediaPage::class, |
||
87 | 'Note' => NotePage::class, |
||
88 | 'Repository' => RepositoryPage::class, |
||
89 | 'Source' => SourcePage::class, |
||
90 | ]; |
||
91 | |||
92 | /** @var int The default access level for this module. It can be changed in the control panel. */ |
||
93 | protected $access_level = Auth::PRIV_USER; |
||
94 | |||
95 | /** |
||
96 | * @var UserService |
||
97 | */ |
||
98 | private $user_service; |
||
99 | |||
100 | /** |
||
101 | * ClippingsCartModule constructor. |
||
102 | * |
||
103 | * @param UserService $user_service |
||
104 | */ |
||
105 | public function __construct(UserService $user_service) |
||
106 | { |
||
107 | $this->user_service = $user_service; |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * How should this module be identified in the control panel, etc.? |
||
112 | * |
||
113 | * @return string |
||
114 | */ |
||
115 | public function title(): string |
||
116 | { |
||
117 | /* I18N: Name of a module */ |
||
118 | return I18N::translate('Clippings cart'); |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * A sentence describing what this module does. |
||
123 | * |
||
124 | * @return string |
||
125 | */ |
||
126 | public function description(): string |
||
127 | { |
||
128 | /* I18N: Description of the “Clippings cart” module */ |
||
129 | return I18N::translate('Select records from your family tree and save them as a GEDCOM file.'); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * The default position for this menu. It can be changed in the control panel. |
||
134 | * |
||
135 | * @return int |
||
136 | */ |
||
137 | public function defaultMenuOrder(): int |
||
138 | { |
||
139 | return 6; |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * A menu, to be added to the main application menu. |
||
144 | * |
||
145 | * @param Tree $tree |
||
146 | * |
||
147 | * @return Menu|null |
||
148 | */ |
||
149 | public function getMenu(Tree $tree): ?Menu |
||
150 | { |
||
151 | /** @var ServerRequestInterface $request */ |
||
152 | $request = app(ServerRequestInterface::class); |
||
153 | |||
154 | $route = $request->getAttribute('route'); |
||
155 | |||
156 | $submenus = [ |
||
157 | new Menu($this->title(), route('module', [ |
||
158 | 'module' => $this->name(), |
||
159 | 'action' => 'Show', |
||
160 | 'tree' => $tree->name(), |
||
161 | ]), 'menu-clippings-cart', ['rel' => 'nofollow']), |
||
162 | ]; |
||
163 | |||
164 | $action = array_search($route, self::ROUTES_WITH_RECORDS); |
||
165 | if ($action !== false) { |
||
166 | $xref = $request->getAttribute('xref'); |
||
167 | assert(is_string($xref)); |
||
168 | |||
169 | $add_route = route('module', [ |
||
170 | 'module' => $this->name(), |
||
171 | 'action' => 'Add' . $action, |
||
172 | 'xref' => $xref, |
||
173 | 'tree' => $tree->name(), |
||
174 | ]); |
||
175 | |||
176 | $submenus[] = new Menu(I18N::translate('Add to the clippings cart'), $add_route, 'menu-clippings-add', ['rel' => 'nofollow']); |
||
177 | } |
||
178 | |||
179 | if (!$this->isCartEmpty($tree)) { |
||
180 | $submenus[] = new Menu(I18N::translate('Empty the clippings cart'), route('module', [ |
||
181 | 'module' => $this->name(), |
||
182 | 'action' => 'Empty', |
||
183 | 'tree' => $tree->name(), |
||
184 | ]), 'menu-clippings-empty', ['rel' => 'nofollow']); |
||
185 | |||
186 | $submenus[] = new Menu(I18N::translate('Download'), route('module', [ |
||
187 | 'module' => $this->name(), |
||
188 | 'action' => 'DownloadForm', |
||
189 | 'tree' => $tree->name(), |
||
190 | ]), 'menu-clippings-download', ['rel' => 'nofollow']); |
||
191 | } |
||
192 | |||
193 | return new Menu($this->title(), '#', 'menu-clippings', ['rel' => 'nofollow'], $submenus); |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * @param ServerRequestInterface $request |
||
198 | * |
||
199 | * @return ResponseInterface |
||
200 | */ |
||
201 | public function postDownloadAction(ServerRequestInterface $request): ResponseInterface |
||
202 | { |
||
203 | $tree = $request->getAttribute('tree'); |
||
204 | assert($tree instanceof Tree); |
||
205 | |||
206 | $privatize_export = $request->getParsedBody()['privatize_export']; |
||
207 | $convert = (bool) ($request->getParsedBody()['convert'] ?? false); |
||
208 | |||
209 | $cart = Session::get('cart', []); |
||
210 | |||
211 | $xrefs = array_keys($cart[$tree->name()] ?? []); |
||
212 | |||
213 | // Create a new/empty .ZIP file |
||
214 | $temp_zip_file = tempnam(sys_get_temp_dir(), 'webtrees-zip-'); |
||
215 | $zip_adapter = new ZipArchiveAdapter($temp_zip_file); |
||
216 | $zip_filesystem = new Filesystem($zip_adapter); |
||
217 | |||
218 | $manager = new MountManager([ |
||
219 | 'media' => $tree->mediaFilesystem(), |
||
220 | 'zip' => $zip_filesystem, |
||
221 | ]); |
||
222 | |||
223 | // Media file prefix |
||
224 | $path = $tree->getPreference('MEDIA_DIRECTORY'); |
||
225 | |||
226 | // GEDCOM file header |
||
227 | $filetext = FunctionsExport::gedcomHeader($tree, $convert ? 'ANSI' : 'UTF-8'); |
||
228 | |||
229 | switch ($privatize_export) { |
||
230 | case 'gedadmin': |
||
231 | $access_level = Auth::PRIV_NONE; |
||
232 | break; |
||
233 | case 'user': |
||
234 | $access_level = Auth::PRIV_USER; |
||
235 | break; |
||
236 | case 'visitor': |
||
237 | $access_level = Auth::PRIV_PRIVATE; |
||
238 | break; |
||
239 | case 'none': |
||
240 | default: |
||
241 | $access_level = Auth::PRIV_HIDE; |
||
242 | break; |
||
243 | } |
||
244 | |||
245 | foreach ($xrefs as $xref) { |
||
246 | $object = GedcomRecord::getInstance($xref, $tree); |
||
247 | // The object may have been deleted since we added it to the cart.... |
||
248 | if ($object instanceof GedcomRecord) { |
||
249 | $record = $object->privatizeGedcom($access_level); |
||
250 | // Remove links to objects that aren't in the cart |
||
251 | preg_match_all('/\n1 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[2-9].*)*/', $record, $matches, PREG_SET_ORDER); |
||
252 | foreach ($matches as $match) { |
||
253 | if (!in_array($match[1], $xrefs, true)) { |
||
254 | $record = str_replace($match[0], '', $record); |
||
255 | } |
||
256 | } |
||
257 | preg_match_all('/\n2 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[3-9].*)*/', $record, $matches, PREG_SET_ORDER); |
||
258 | foreach ($matches as $match) { |
||
259 | if (!in_array($match[1], $xrefs, true)) { |
||
260 | $record = str_replace($match[0], '', $record); |
||
261 | } |
||
262 | } |
||
263 | preg_match_all('/\n3 ' . Gedcom::REGEX_TAG . ' @(' . Gedcom::REGEX_XREF . ')@(\n[4-9].*)*/', $record, $matches, PREG_SET_ORDER); |
||
264 | foreach ($matches as $match) { |
||
265 | if (!in_array($match[1], $xrefs, true)) { |
||
266 | $record = str_replace($match[0], '', $record); |
||
267 | } |
||
268 | } |
||
269 | |||
270 | if ($object instanceof Individual || $object instanceof Family) { |
||
271 | $filetext .= $record . "\n"; |
||
272 | $filetext .= "1 SOUR @WEBTREES@\n"; |
||
273 | $filetext .= '2 PAGE ' . $object->url() . "\n"; |
||
274 | } elseif ($object instanceof Source) { |
||
275 | $filetext .= $record . "\n"; |
||
276 | $filetext .= '1 NOTE ' . $object->url() . "\n"; |
||
277 | } elseif ($object instanceof Media) { |
||
278 | // Add the media files to the archive |
||
279 | foreach ($object->mediaFiles() as $media_file) { |
||
280 | $from = 'media://' . $media_file->filename(); |
||
281 | $to = 'zip://' . $path . $media_file->filename(); |
||
282 | if (!$media_file->isExternal() && $manager->has($from)) { |
||
283 | $manager->copy($from, $to); |
||
284 | } |
||
285 | } |
||
286 | $filetext .= $record . "\n"; |
||
287 | } else { |
||
288 | $filetext .= $record . "\n"; |
||
289 | } |
||
290 | } |
||
291 | } |
||
292 | |||
293 | $base_url = $request->getAttribute('base_url'); |
||
294 | |||
295 | // Create a source, to indicate the source of the data. |
||
296 | $filetext .= "0 @WEBTREES@ SOUR\n1 TITL " . $base_url . "\n"; |
||
297 | $author = $this->user_service->find((int) $tree->getPreference('CONTACT_USER_ID')); |
||
298 | if ($author !== null) { |
||
299 | $filetext .= '1 AUTH ' . $author->realName() . "\n"; |
||
300 | } |
||
301 | $filetext .= "0 TRLR\n"; |
||
302 | |||
303 | // Make sure the preferred line endings are used |
||
304 | $filetext = str_replace('\n', Gedcom::EOL, $filetext); |
||
305 | |||
306 | if ($convert) { |
||
307 | $filetext = utf8_decode($filetext); |
||
308 | } |
||
309 | |||
310 | // Finally add the GEDCOM file to the .ZIP file. |
||
311 | $zip_filesystem->write('clippings.ged', $filetext); |
||
312 | |||
313 | // Need to force-close ZipArchive filesystems. |
||
314 | $zip_adapter->getArchive()->close(); |
||
315 | |||
316 | // Use a stream, so that we do not have to load the entire file into memory. |
||
317 | $stream = app(StreamFactoryInterface::class)->createStreamFromFile($temp_zip_file); |
||
318 | |||
319 | /** @var ResponseFactoryInterface $response_factory */ |
||
320 | $response_factory = app(ResponseFactoryInterface::class); |
||
321 | |||
322 | return $response_factory->createResponse() |
||
323 | ->withBody($stream) |
||
324 | ->withHeader('Content-Type', 'application/zip') |
||
325 | ->withHeader('Content-Disposition', 'attachment; filename="clippings.zip'); |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * @param ServerRequestInterface $request |
||
330 | * |
||
331 | * @return ResponseInterface |
||
332 | */ |
||
333 | public function getDownloadFormAction(ServerRequestInterface $request): ResponseInterface |
||
334 | { |
||
335 | $tree = $request->getAttribute('tree'); |
||
336 | assert($tree instanceof Tree); |
||
337 | |||
338 | $user = $request->getAttribute('user'); |
||
339 | $title = I18N::translate('Family tree clippings cart') . ' — ' . I18N::translate('Download'); |
||
340 | |||
341 | return $this->viewResponse('modules/clippings/download', [ |
||
342 | 'is_manager' => Auth::isManager($tree, $user), |
||
343 | 'is_member' => Auth::isMember($tree, $user), |
||
344 | 'module' => $this->name(), |
||
345 | 'title' => $title, |
||
346 | 'tree' => $tree, |
||
347 | ]); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * @param ServerRequestInterface $request |
||
352 | * |
||
353 | * @return ResponseInterface |
||
354 | */ |
||
355 | public function getEmptyAction(ServerRequestInterface $request): ResponseInterface |
||
356 | { |
||
357 | $tree = $request->getAttribute('tree'); |
||
358 | assert($tree instanceof Tree); |
||
359 | |||
360 | $cart = Session::get('cart', []); |
||
361 | $cart[$tree->name()] = []; |
||
362 | Session::put('cart', $cart); |
||
363 | |||
364 | $url = route('module', [ |
||
365 | 'module' => $this->name(), |
||
366 | 'action' => 'Show', |
||
367 | 'tree' => $tree->name(), |
||
368 | ]); |
||
369 | |||
370 | return redirect($url); |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * @param ServerRequestInterface $request |
||
375 | * |
||
376 | * @return ResponseInterface |
||
377 | */ |
||
378 | public function postRemoveAction(ServerRequestInterface $request): ResponseInterface |
||
379 | { |
||
380 | $tree = $request->getAttribute('tree'); |
||
381 | assert($tree instanceof Tree); |
||
382 | |||
383 | $xref = $request->getQueryParams()['xref']; |
||
384 | |||
385 | $cart = Session::get('cart', []); |
||
386 | unset($cart[$tree->name()][$xref]); |
||
387 | Session::put('cart', $cart); |
||
388 | |||
389 | $url = route('module', [ |
||
390 | 'module' => $this->name(), |
||
391 | 'action' => 'Show', |
||
392 | 'tree' => $tree->name(), |
||
393 | ]); |
||
394 | |||
395 | return redirect($url); |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * @param ServerRequestInterface $request |
||
400 | * |
||
401 | * @return ResponseInterface |
||
402 | */ |
||
403 | public function getShowAction(ServerRequestInterface $request): ResponseInterface |
||
404 | { |
||
405 | $tree = $request->getAttribute('tree'); |
||
406 | assert($tree instanceof Tree); |
||
407 | |||
408 | return $this->viewResponse('modules/clippings/show', [ |
||
409 | 'records' => $this->allRecordsInCart($tree), |
||
410 | 'title' => I18N::translate('Family tree clippings cart'), |
||
411 | 'tree' => $tree, |
||
412 | ]); |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * @param ServerRequestInterface $request |
||
417 | * |
||
418 | * @return ResponseInterface |
||
419 | */ |
||
420 | public function getAddFamilyAction(ServerRequestInterface $request): ResponseInterface |
||
421 | { |
||
422 | $tree = $request->getAttribute('tree'); |
||
423 | assert($tree instanceof Tree); |
||
424 | |||
425 | $xref = $request->getQueryParams()['xref']; |
||
426 | |||
427 | $family = Family::getInstance($xref, $tree); |
||
428 | |||
429 | if ($family === null) { |
||
430 | throw new FamilyNotFoundException(); |
||
431 | } |
||
432 | |||
433 | $options = $this->familyOptions($family); |
||
434 | |||
435 | $title = I18N::translate('Add %s to the clippings cart', $family->fullName()); |
||
436 | |||
437 | return $this->viewResponse('modules/clippings/add-options', [ |
||
438 | 'options' => $options, |
||
439 | 'default' => key($options), |
||
440 | 'record' => $family, |
||
441 | 'title' => $title, |
||
442 | 'tree' => $tree, |
||
443 | ]); |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * @param Family $family |
||
448 | * |
||
449 | * @return string[] |
||
450 | */ |
||
451 | private function familyOptions(Family $family): array |
||
452 | { |
||
453 | $name = strip_tags($family->fullName()); |
||
454 | |||
455 | return [ |
||
456 | 'parents' => $name, |
||
457 | /* I18N: %s is a family (husband + wife) */ |
||
458 | 'members' => I18N::translate('%s and their children', $name), |
||
459 | /* I18N: %s is a family (husband + wife) */ |
||
460 | 'descendants' => I18N::translate('%s and their descendants', $name), |
||
461 | ]; |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * @param ServerRequestInterface $request |
||
466 | * |
||
467 | * @return ResponseInterface |
||
468 | */ |
||
469 | public function postAddFamilyAction(ServerRequestInterface $request): ResponseInterface |
||
470 | { |
||
471 | $tree = $request->getAttribute('tree'); |
||
472 | assert($tree instanceof Tree); |
||
473 | |||
474 | $xref = $request->getQueryParams()['xref']; |
||
475 | $option = $request->getParsedBody()['option']; |
||
476 | |||
477 | $family = Family::getInstance($xref, $tree); |
||
478 | |||
479 | if ($family === null) { |
||
480 | throw new FamilyNotFoundException(); |
||
481 | } |
||
482 | |||
483 | switch ($option) { |
||
484 | case 'parents': |
||
485 | $this->addFamilyToCart($family); |
||
486 | break; |
||
487 | |||
488 | case 'members': |
||
489 | $this->addFamilyAndChildrenToCart($family); |
||
490 | break; |
||
491 | |||
492 | case 'descendants': |
||
493 | $this->addFamilyAndDescendantsToCart($family); |
||
494 | break; |
||
495 | } |
||
496 | |||
497 | return redirect($family->url()); |
||
498 | } |
||
499 | |||
500 | /** |
||
501 | * @param Family $family |
||
502 | * |
||
503 | * @return void |
||
504 | */ |
||
505 | private function addFamilyToCart(Family $family): void |
||
506 | { |
||
507 | $this->addRecordToCart($family); |
||
508 | |||
509 | foreach ($family->spouses() as $spouse) { |
||
510 | $this->addRecordToCart($spouse); |
||
511 | } |
||
512 | } |
||
513 | |||
514 | /** |
||
515 | * @param Family $family |
||
516 | * |
||
517 | * @return void |
||
518 | */ |
||
519 | private function addFamilyAndChildrenToCart(Family $family): void |
||
520 | { |
||
521 | $this->addRecordToCart($family); |
||
522 | |||
523 | foreach ($family->spouses() as $spouse) { |
||
524 | $this->addRecordToCart($spouse); |
||
525 | } |
||
526 | foreach ($family->children() as $child) { |
||
527 | $this->addRecordToCart($child); |
||
528 | } |
||
529 | } |
||
530 | |||
531 | /** |
||
532 | * @param Family $family |
||
533 | * |
||
534 | * @return void |
||
535 | */ |
||
536 | private function addFamilyAndDescendantsToCart(Family $family): void |
||
537 | { |
||
538 | $this->addRecordToCart($family); |
||
539 | |||
540 | foreach ($family->spouses() as $spouse) { |
||
541 | $this->addRecordToCart($spouse); |
||
542 | } |
||
543 | foreach ($family->children() as $child) { |
||
544 | $this->addRecordToCart($child); |
||
545 | foreach ($child->spouseFamilies() as $child_family) { |
||
546 | $this->addFamilyAndDescendantsToCart($child_family); |
||
547 | } |
||
548 | } |
||
549 | } |
||
550 | |||
551 | /** |
||
552 | * @param ServerRequestInterface $request |
||
553 | * |
||
554 | * @return ResponseInterface |
||
555 | */ |
||
556 | public function getAddIndividualAction(ServerRequestInterface $request): ResponseInterface |
||
557 | { |
||
558 | $tree = $request->getAttribute('tree'); |
||
559 | assert($tree instanceof Tree); |
||
560 | |||
561 | $xref = $request->getQueryParams()['xref']; |
||
562 | |||
563 | $individual = Individual::getInstance($xref, $tree); |
||
564 | |||
565 | if ($individual === null) { |
||
566 | throw new IndividualNotFoundException(); |
||
567 | } |
||
568 | |||
569 | $options = $this->individualOptions($individual); |
||
570 | |||
571 | $title = I18N::translate('Add %s to the clippings cart', $individual->fullName()); |
||
572 | |||
573 | return $this->viewResponse('modules/clippings/add-options', [ |
||
574 | 'options' => $options, |
||
575 | 'default' => key($options), |
||
576 | 'record' => $individual, |
||
577 | 'title' => $title, |
||
578 | 'tree' => $tree, |
||
579 | ]); |
||
580 | } |
||
581 | |||
582 | /** |
||
583 | * @param Individual $individual |
||
584 | * |
||
585 | * @return string[] |
||
586 | */ |
||
587 | private function individualOptions(Individual $individual): array |
||
588 | { |
||
589 | $name = strip_tags($individual->fullName()); |
||
590 | |||
591 | if ($individual->sex() === 'F') { |
||
592 | return [ |
||
593 | 'self' => $name, |
||
594 | 'parents' => I18N::translate('%s, her parents and siblings', $name), |
||
595 | 'spouses' => I18N::translate('%s, her spouses and children', $name), |
||
596 | 'ancestors' => I18N::translate('%s and her ancestors', $name), |
||
597 | 'ancestor_families' => I18N::translate('%s, her ancestors and their families', $name), |
||
598 | 'descendants' => I18N::translate('%s, her spouses and descendants', $name), |
||
599 | ]; |
||
600 | } |
||
601 | |||
602 | return [ |
||
603 | 'self' => $name, |
||
604 | 'parents' => I18N::translate('%s, his parents and siblings', $name), |
||
605 | 'spouses' => I18N::translate('%s, his spouses and children', $name), |
||
606 | 'ancestors' => I18N::translate('%s and his ancestors', $name), |
||
607 | 'ancestor_families' => I18N::translate('%s, his ancestors and their families', $name), |
||
608 | 'descendants' => I18N::translate('%s, his spouses and descendants', $name), |
||
609 | ]; |
||
610 | } |
||
611 | |||
612 | /** |
||
613 | * @param ServerRequestInterface $request |
||
614 | * |
||
615 | * @return ResponseInterface |
||
616 | */ |
||
617 | public function postAddIndividualAction(ServerRequestInterface $request): ResponseInterface |
||
618 | { |
||
619 | $tree = $request->getAttribute('tree'); |
||
620 | assert($tree instanceof Tree); |
||
621 | |||
622 | $xref = $request->getQueryParams()['xref']; |
||
623 | $option = $request->getParsedBody()['option']; |
||
624 | |||
625 | $individual = Individual::getInstance($xref, $tree); |
||
626 | |||
627 | if ($individual === null) { |
||
628 | throw new IndividualNotFoundException(); |
||
629 | } |
||
630 | |||
631 | switch ($option) { |
||
632 | case 'self': |
||
633 | $this->addRecordToCart($individual); |
||
634 | break; |
||
635 | |||
636 | case 'parents': |
||
637 | foreach ($individual->childFamilies() as $family) { |
||
638 | $this->addFamilyAndChildrenToCart($family); |
||
639 | } |
||
640 | break; |
||
641 | |||
642 | case 'spouses': |
||
643 | foreach ($individual->spouseFamilies() as $family) { |
||
644 | $this->addFamilyAndChildrenToCart($family); |
||
645 | } |
||
646 | break; |
||
647 | |||
648 | case 'ancestors': |
||
649 | $this->addAncestorsToCart($individual); |
||
650 | break; |
||
651 | |||
652 | case 'ancestor_families': |
||
653 | $this->addAncestorFamiliesToCart($individual); |
||
654 | break; |
||
655 | |||
656 | case 'descendants': |
||
657 | foreach ($individual->spouseFamilies() as $family) { |
||
658 | $this->addFamilyAndDescendantsToCart($family); |
||
659 | } |
||
660 | break; |
||
661 | } |
||
662 | |||
663 | return redirect($individual->url()); |
||
664 | } |
||
665 | |||
666 | /** |
||
667 | * @param Individual $individual |
||
668 | * |
||
669 | * @return void |
||
670 | */ |
||
671 | private function addAncestorsToCart(Individual $individual): void |
||
672 | { |
||
673 | $this->addRecordToCart($individual); |
||
674 | |||
675 | foreach ($individual->childFamilies() as $family) { |
||
676 | foreach ($family->spouses() as $parent) { |
||
677 | $this->addAncestorsToCart($parent); |
||
678 | } |
||
679 | } |
||
680 | } |
||
681 | |||
682 | /** |
||
683 | * @param Individual $individual |
||
684 | * |
||
685 | * @return void |
||
686 | */ |
||
687 | private function addAncestorFamiliesToCart(Individual $individual): void |
||
688 | { |
||
689 | foreach ($individual->childFamilies() as $family) { |
||
690 | $this->addFamilyAndChildrenToCart($family); |
||
691 | foreach ($family->spouses() as $parent) { |
||
692 | $this->addAncestorsToCart($parent); |
||
693 | } |
||
694 | } |
||
695 | } |
||
696 | |||
697 | /** |
||
698 | * @param ServerRequestInterface $request |
||
699 | * |
||
700 | * @return ResponseInterface |
||
701 | */ |
||
702 | public function getAddMediaAction(ServerRequestInterface $request): ResponseInterface |
||
703 | { |
||
704 | $tree = $request->getAttribute('tree'); |
||
705 | assert($tree instanceof Tree); |
||
706 | |||
707 | $xref = $request->getQueryParams()['xref']; |
||
708 | |||
709 | $media = Media::getInstance($xref, $tree); |
||
710 | |||
711 | if ($media === null) { |
||
712 | throw new MediaNotFoundException(); |
||
713 | } |
||
714 | |||
715 | $options = $this->mediaOptions($media); |
||
716 | |||
717 | $title = I18N::translate('Add %s to the clippings cart', $media->fullName()); |
||
718 | |||
719 | return $this->viewResponse('modules/clippings/add-options', [ |
||
720 | 'options' => $options, |
||
721 | 'default' => key($options), |
||
722 | 'record' => $media, |
||
723 | 'title' => $title, |
||
724 | 'tree' => $tree, |
||
725 | ]); |
||
726 | } |
||
727 | |||
728 | /** |
||
729 | * @param Media $media |
||
730 | * |
||
731 | * @return string[] |
||
732 | */ |
||
733 | private function mediaOptions(Media $media): array |
||
734 | { |
||
735 | $name = strip_tags($media->fullName()); |
||
736 | |||
737 | return [ |
||
738 | 'self' => $name, |
||
739 | ]; |
||
740 | } |
||
741 | |||
742 | /** |
||
743 | * @param ServerRequestInterface $request |
||
744 | * |
||
745 | * @return ResponseInterface |
||
746 | */ |
||
747 | public function postAddMediaAction(ServerRequestInterface $request): ResponseInterface |
||
748 | { |
||
749 | $tree = $request->getAttribute('tree'); |
||
750 | assert($tree instanceof Tree); |
||
751 | |||
752 | $xref = $request->getQueryParams()['xref']; |
||
753 | |||
754 | $media = Media::getInstance($xref, $tree); |
||
755 | |||
756 | if ($media === null) { |
||
757 | throw new MediaNotFoundException(); |
||
758 | } |
||
759 | |||
760 | $this->addRecordToCart($media); |
||
761 | |||
762 | return redirect($media->url()); |
||
763 | } |
||
764 | |||
765 | /** |
||
766 | * @param ServerRequestInterface $request |
||
767 | * |
||
768 | * @return ResponseInterface |
||
769 | */ |
||
770 | public function getAddNoteAction(ServerRequestInterface $request): ResponseInterface |
||
793 | ]); |
||
794 | } |
||
795 | |||
796 | /** |
||
797 | * @param Note $note |
||
798 | * |
||
799 | * @return string[] |
||
800 | */ |
||
801 | private function noteOptions(Note $note): array |
||
802 | { |
||
803 | $name = strip_tags($note->fullName()); |
||
804 | |||
805 | return [ |
||
806 | 'self' => $name, |
||
807 | ]; |
||
808 | } |
||
809 | |||
810 | /** |
||
811 | * @param ServerRequestInterface $request |
||
812 | * |
||
813 | * @return ResponseInterface |
||
814 | */ |
||
815 | public function postAddNoteAction(ServerRequestInterface $request): ResponseInterface |
||
816 | { |
||
817 | $tree = $request->getAttribute('tree'); |
||
818 | assert($tree instanceof Tree); |
||
819 | |||
820 | $xref = $request->getQueryParams()['xref']; |
||
821 | |||
822 | $note = Note::getInstance($xref, $tree); |
||
823 | |||
824 | if ($note === null) { |
||
825 | throw new NoteNotFoundException(); |
||
826 | } |
||
827 | |||
828 | $this->addRecordToCart($note); |
||
829 | |||
830 | return redirect($note->url()); |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * @param ServerRequestInterface $request |
||
835 | * |
||
836 | * @return ResponseInterface |
||
837 | */ |
||
838 | public function getAddRepositoryAction(ServerRequestInterface $request): ResponseInterface |
||
861 | ]); |
||
862 | } |
||
863 | |||
864 | /** |
||
865 | * @param Repository $repository |
||
866 | * |
||
867 | * @return string[] |
||
868 | */ |
||
869 | private function repositoryOptions(Repository $repository): array |
||
870 | { |
||
871 | $name = strip_tags($repository->fullName()); |
||
872 | |||
873 | return [ |
||
874 | 'self' => $name, |
||
875 | ]; |
||
876 | } |
||
877 | |||
878 | /** |
||
879 | * @param ServerRequestInterface $request |
||
880 | * |
||
881 | * @return ResponseInterface |
||
882 | */ |
||
883 | public function postAddRepositoryAction(ServerRequestInterface $request): ResponseInterface |
||
884 | { |
||
885 | $tree = $request->getAttribute('tree'); |
||
886 | assert($tree instanceof Tree); |
||
887 | |||
888 | $xref = $request->getQueryParams()['xref']; |
||
889 | |||
890 | $repository = Repository::getInstance($xref, $tree); |
||
891 | |||
892 | if ($repository === null) { |
||
893 | throw new RepositoryNotFoundException(); |
||
894 | } |
||
895 | |||
896 | $this->addRecordToCart($repository); |
||
897 | |||
898 | return redirect($repository->url()); |
||
899 | } |
||
900 | |||
901 | /** |
||
902 | * @param ServerRequestInterface $request |
||
903 | * |
||
904 | * @return ResponseInterface |
||
905 | */ |
||
906 | public function getAddSourceAction(ServerRequestInterface $request): ResponseInterface |
||
907 | { |
||
908 | $tree = $request->getAttribute('tree'); |
||
909 | assert($tree instanceof Tree); |
||
910 | |||
911 | $xref = $request->getQueryParams()['xref']; |
||
912 | |||
913 | $source = Source::getInstance($xref, $tree); |
||
914 | |||
915 | if ($source === null) { |
||
916 | throw new SourceNotFoundException(); |
||
917 | } |
||
918 | |||
919 | $options = $this->sourceOptions($source); |
||
920 | |||
921 | $title = I18N::translate('Add %s to the clippings cart', $source->fullName()); |
||
922 | |||
923 | return $this->viewResponse('modules/clippings/add-options', [ |
||
924 | 'options' => $options, |
||
925 | 'default' => key($options), |
||
926 | 'record' => $source, |
||
927 | 'title' => $title, |
||
928 | 'tree' => $tree, |
||
929 | ]); |
||
930 | } |
||
931 | |||
932 | /** |
||
933 | * @param Source $source |
||
934 | * |
||
935 | * @return string[] |
||
936 | */ |
||
937 | private function sourceOptions(Source $source): array |
||
938 | { |
||
939 | $name = strip_tags($source->fullName()); |
||
940 | |||
941 | return [ |
||
942 | 'only' => strip_tags($source->fullName()), |
||
943 | 'linked' => I18N::translate('%s and the individuals that reference it.', $name), |
||
944 | ]; |
||
945 | } |
||
946 | |||
947 | /** |
||
948 | * @param ServerRequestInterface $request |
||
949 | * |
||
950 | * @return ResponseInterface |
||
951 | */ |
||
952 | public function postAddSourceAction(ServerRequestInterface $request): ResponseInterface |
||
953 | { |
||
954 | $tree = $request->getAttribute('tree'); |
||
955 | assert($tree instanceof Tree); |
||
956 | |||
957 | $xref = $request->getQueryParams()['xref']; |
||
958 | $option = $request->getParsedBody()['option']; |
||
959 | |||
960 | $source = Source::getInstance($xref, $tree); |
||
961 | |||
962 | if ($source === null) { |
||
963 | throw new SourceNotFoundException(); |
||
964 | } |
||
965 | |||
966 | $this->addRecordToCart($source); |
||
967 | |||
968 | if ($option === 'linked') { |
||
969 | foreach ($source->linkedIndividuals('SOUR') as $individual) { |
||
970 | $this->addRecordToCart($individual); |
||
971 | } |
||
972 | foreach ($source->linkedFamilies('SOUR') as $family) { |
||
973 | $this->addRecordToCart($family); |
||
974 | } |
||
975 | } |
||
976 | |||
977 | return redirect($source->url()); |
||
978 | } |
||
979 | |||
980 | /** |
||
981 | * Get all the records in the cart. |
||
982 | * |
||
983 | * @param Tree $tree |
||
984 | * |
||
985 | * @return GedcomRecord[] |
||
986 | */ |
||
987 | private function allRecordsInCart(Tree $tree): array |
||
988 | { |
||
989 | $cart = Session::get('cart', []); |
||
990 | |||
991 | $xrefs = array_keys($cart[$tree->name()] ?? []); |
||
992 | |||
993 | // Fetch all the records in the cart. |
||
994 | $records = array_map(static function (string $xref) use ($tree): ?GedcomRecord { |
||
995 | return GedcomRecord::getInstance($xref, $tree); |
||
996 | }, $xrefs); |
||
997 | |||
998 | // Some records may have been deleted after they were added to the cart. |
||
999 | $records = array_filter($records); |
||
1000 | |||
1001 | // Group and sort. |
||
1002 | uasort($records, static function (GedcomRecord $x, GedcomRecord $y): int { |
||
1003 | return $x::RECORD_TYPE <=> $y::RECORD_TYPE ?: GedcomRecord::nameComparator()($x, $y); |
||
1004 | }); |
||
1005 | |||
1006 | return $records; |
||
1007 | } |
||
1008 | |||
1009 | /** |
||
1010 | * Add a record (and direclty linked sources, notes, etc. to the cart. |
||
1011 | * |
||
1012 | * @param GedcomRecord $record |
||
1013 | * |
||
1014 | * @return void |
||
1015 | */ |
||
1016 | private function addRecordToCart(GedcomRecord $record): void |
||
1033 | } |
||
1034 | |||
1035 | /** |
||
1036 | * @param Tree $tree |
||
1037 | * |
||
1038 | * @return bool |
||
1039 | */ |
||
1040 | private function isCartEmpty(Tree $tree): bool |
||
1041 | { |
||
1042 | $cart = Session::get('cart', []); |
||
1043 | $contents = $cart[$tree->name()] ?? []; |
||
1044 | |||
1045 | return $contents === []; |
||
1046 | } |
||
1047 | } |
||
1048 |