xref: /webtrees/app/Module/ClippingsCartModule.php (revision 571e6fcac743cc9b048486db27eb10ae509eb172)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16namespace Fisharebest\Webtrees\Module;
17
18use Fisharebest\Webtrees\Auth;
19use Fisharebest\Webtrees\Controller\PageController;
20use Fisharebest\Webtrees\Family;
21use Fisharebest\Webtrees\Filter;
22use Fisharebest\Webtrees\GedcomRecord;
23use Fisharebest\Webtrees\Html;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Individual;
26use Fisharebest\Webtrees\Menu;
27use Fisharebest\Webtrees\Module\ClippingsCart\ClippingsCartController;
28use Fisharebest\Webtrees\Session;
29
30/**
31 * Class ClippingsCartModule
32 */
33class ClippingsCartModule extends AbstractModule implements ModuleMenuInterface, ModuleSidebarInterface {
34	/** {@inheritdoc} */
35	public function getTitle() {
36		return /* I18N: Name of a module */
37			I18N::translate('Clippings cart');
38	}
39
40	/** {@inheritdoc} */
41	public function getDescription() {
42		return /* I18N: Description of the “Clippings cart” module */
43			I18N::translate('Select records from your family tree and save them as a GEDCOM file.');
44	}
45
46	/**
47	 * What is the default access level for this module?
48	 *
49	 * Some modules are aimed at admins or managers, and are not generally shown to users.
50	 *
51	 * @return int
52	 */
53	public function defaultAccessLevel() {
54		return Auth::PRIV_USER;
55	}
56
57	/**
58	 * This is a general purpose hook, allowing modules to respond to routes
59	 * of the form module.php?mod=FOO&mod_action=BAR
60	 *
61	 * @param string $mod_action
62	 */
63	public function modAction($mod_action) {
64		switch ($mod_action) {
65			case 'ajax':
66				$html = $this->getSidebarAjaxContent();
67				header('Content-Type: text/html; charset=UTF-8');
68				echo $html;
69				break;
70			case 'index':
71				global $controller, $WT_TREE;
72
73				$MAX_PEDIGREE_GENERATIONS = $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS');
74
75				$clip_ctrl = new ClippingsCartController;
76				$cart      = Session::get('cart');
77
78				$controller = new PageController;
79				$controller
80					->setPageTitle($this->getTitle())
81					->pageHeader();
82
83				echo '<script>';
84				echo 'function radAncestors(elementid) {var radFamilies=document.getElementById(elementid);radFamilies.checked=true;}';
85				echo '</script>';
86				echo '<div class="clipping-cart">';
87
88				if (!$cart[$WT_TREE->getTreeId()]) {
89					echo '<h2>', I18N::translate('Family tree clippings cart'), '</h2>';
90				}
91
92				if ($clip_ctrl->action == 'add') {
93					$record = GedcomRecord::getInstance($clip_ctrl->id, $WT_TREE);
94					if ($clip_ctrl->type === 'FAM') { ?>
95					<form class="wt-page-options wt-page-options-clipping-cart hidden-print" action="module.php">
96						<input type="hidden" name="mod" value="clippings">
97						<input type="hidden" name="mod_action" value="index">
98						<input type="hidden" name="id" value="<?= $clip_ctrl->id ?>">
99						<input type="hidden" name="type" value="<?= $clip_ctrl->type ?>">
100						<input type="hidden" name="action" value="add1">
101						<table class="add-to center">
102							<thead>
103								<tr>
104									<td class="topbottombar">
105										<?= I18N::translate('Add to the clippings cart') ?>
106									</td>
107								</tr>
108							</thead>
109							<tbody>
110								<tr>
111									<td class="optionbox">
112										<input type="radio" name="others" value="parents">
113										<?= $record->getFullName() ?>
114									</td>
115								</tr>
116								<tr>
117									<td class="optionbox">
118										<input type="radio" name="others" value="members" checked>
119										<?= /* I18N: %s is a family (husband + wife) */
120											I18N::translate('%s and their children', $record->getFullName()) ?>
121									</td>
122								</tr>
123								<tr>
124									<td class="optionbox">
125										<input type="radio" name="others" value="descendants">
126										<?= /* I18N: %s is a family (husband + wife) */
127											I18N::translate('%s and their descendants', $record->getFullName()) ?>
128									</td>
129								</tr>
130							</tbody>
131							<tfoot>
132								<tr>
133									<td class="topbottombar"><input type="submit" value="<?= I18N::translate('continue') ?>">
134									</td>
135								</tr>
136							</tfoot>
137						</table>
138					</form>
139				</div>
140				<?php } elseif ($clip_ctrl->type === 'INDI') { ?>
141					<form class="wt-page-options wt-page-options-clipping-cart hidden-print" action="module.php">
142						<input type="hidden" name="mod" value="clippings">
143						<input type="hidden" name="mod_action" value="index">
144						<input type="hidden" name="id" value="<?= $clip_ctrl->id ?>">
145						<input type="hidden" name="type" value="<?= $clip_ctrl->type ?>">
146						<input type="hidden" name="action" value="add1">
147						<table class="add-to center">
148							<thead>
149								<tr>
150									<td class="topbottombar">
151										<?= I18N::translate('Add to the clippings cart') ?>
152									</td>
153								</tr>
154							</thead>
155							<tbody>
156								<tr>
157									<td class="optionbox">
158										<label>
159											<input type="radio" name="others" checked value="none">
160											<?= $record->getFullName() ?>
161										</label>
162									</td>
163								</tr>
164								<tr>
165									<td class="optionbox">
166										<label>
167											<input type="radio" name="others" value="parents">
168											<?php
169												if ($record->getSex() === 'F') {
170													echo /* I18N: %s is a woman's name */
171													I18N::translate('%s, her parents and siblings', $record->getFullName());
172												} else {
173													echo /* I18N: %s is a man's name */
174													I18N::translate('%s, his parents and siblings', $record->getFullName());
175												}
176												?>
177										</label>
178									</td>
179								</tr>
180								<tr>
181									<td class="optionbox">
182										<label>
183											<input type="radio" name="others" value="members">
184											<?php
185												if ($record->getSex() === 'F') {
186													echo /* I18N: %s is a woman's name */
187													I18N::translate('%s, her spouses and children', $record->getFullName());
188												} else {
189													echo /* I18N: %s is a man's name */
190													I18N::translate('%s, his spouses and children', $record->getFullName());
191												}
192												?>
193										</label>
194									</td>
195								</tr>
196								<tr>
197									<td class="optionbox">
198										<label>
199											<input type="radio" name="others" value="ancestors" id="ancestors">
200											<?php
201												if ($record->getSex() === 'F') {
202													echo /* I18N: %s is a woman's name */
203													I18N::translate('%s and her ancestors', $record->getFullName());
204												} else {
205													echo /* I18N: %s is a man's name */
206													I18N::translate('%s and his ancestors', $record->getFullName());
207												}
208												?>
209										</label>
210										<br>
211										<?= I18N::translate('Number of generations') ?>
212											<input type="text" size="5" name="level1" value="<?= $MAX_PEDIGREE_GENERATIONS ?>" onfocus="radAncestors('ancestors');">
213									</td>
214								</tr>
215								<tr>
216									<td class="optionbox">
217										<label>
218											<input type="radio" name="others" value="ancestorsfamilies" id="ancestorsfamilies">
219											<?php
220												if ($record->getSex() === 'F') {
221													echo /* I18N: %s is a woman's name */
222													I18N::translate('%s, her ancestors and their families', $record->getFullName());
223												} else {
224													echo /* I18N: %s is a man's name */
225													I18N::translate('%s, his ancestors and their families', $record->getFullName());
226												}
227												?>
228										</label>
229										<br>
230										<?= I18N::translate('Number of generations') ?>
231											<input type="text" size="5" name="level2" value="<?= $MAX_PEDIGREE_GENERATIONS ?>" onfocus="radAncestors('ancestorsfamilies');">
232									</td>
233								</tr>
234								<tr>
235									<td class="optionbox">
236										<label>
237											<input type="radio" name="others" value="descendants" id="descendants">
238											<?php
239												if ($record->getSex() === 'F') {
240													echo /* I18N: %s is a woman's name */
241													I18N::translate('%s, her spouses and descendants', $record->getFullName());
242												} else {
243													echo /* I18N: %s is a man's name */
244													I18N::translate('%s, his spouses and descendants', $record->getFullName());
245												}
246												?>
247										</label>
248										<br>
249										<?= I18N::translate('Number of generations') ?>
250											<input type="text" size="5" name="level3" value="<?= $MAX_PEDIGREE_GENERATIONS ?>" onfocus="radAncestors('descendants');">
251									</td>
252								</tr>
253							</tbody>
254							<tfoot>
255								<tr>
256									<td class="topbottombar">
257										<input type="submit" value="<?= I18N::translate('continue') ?>">
258									</td>
259								</tr>
260							</tfoot>
261						</table>
262					</form>
263				</div>
264				<?php } elseif ($clip_ctrl->type === 'SOUR') { ?>
265					<form class="wt-page-options wt-page-options-clipping-cart hidden-print" action="module.php">
266						<input type="hidden" name="mod" value="clippings">
267						<input type="hidden" name="mod_action" value="index">
268						<input type="hidden" name="id" value="<?= $clip_ctrl->id ?>">
269						<input type="hidden" name="type" value="<?= $clip_ctrl->type ?>">
270						<input type="hidden" name="action" value="add1">
271						<table class="add-to center">
272							<thead>
273								<tr>
274									<td class="topbottombar">
275										<?= I18N::translate('Add to the clippings cart') ?>
276									</td>
277								</tr>
278							</thead>
279							<tbody>
280								<tr>
281									<td class="optionbox">
282										<label>
283											<input type="radio" name="others" checked value="none">
284											<?= $record->getFullName() ?>
285										</label>
286									</td>
287								</tr>
288								<tr>
289									<td class="optionbox">
290										<label>
291											<input type="radio" name="others" value="linked">
292											<?= /* I18N: %s is the name of a source */
293												I18N::translate('%s and the individuals that reference it.', $record->getFullName()) ?>
294										</label>
295									</td>
296								</tr>
297							</tbody>
298							<tfoot>
299								<tr>
300									<td class="topbottombar">
301										<input type="submit" value="<?= I18N::translate('continue') ?>">
302									</td>
303								</tr>
304							</tfoot>
305						</table>
306					</form>
307				</div>
308				<?php }
309				}
310
311				if (!$cart[$WT_TREE->getTreeId()]) {
312					if ($clip_ctrl->action != 'add') {
313						echo '<div class="center">';
314						echo I18N::translate('The clippings cart allows you to take extracts from this family tree and download them as a GEDCOM file.');
315						echo '</div>';
316						?>
317					<form class="wt-page-options wt-page-options-clipping-cart hidden-print" name="addin" action="module.php">
318						<input type="hidden" name="mod" value="clippings">
319						<input type="hidden" name="mod_action" value="index">
320						<table class="add-to center">
321							<thead>
322								<tr>
323									<td colspan="2" class="topbottombar">
324										<?= I18N::translate('Add to the clippings cart') ?>
325									</td>
326								</tr>
327							</thead>
328							<tbody>
329								<tr>
330									<td class="optionbox">
331										<input type="hidden" name="action" value="add">
332										<input type="text" data-autocomplete-type="IFSRO" name="id" id="cart_item_id" size="5">
333									</td>
334									<td class="optionbox">
335										<input type="submit" value="<?= /* I18N: A button label. */ I18N::translate('add') ?>">
336									</td>
337								</tr>
338							</tbody>
339						</table>
340					</form>
341				</div>
342					<?php
343					}
344					echo '<div class="center">';
345					// -- end new lines
346					echo I18N::translate('Your clippings cart is empty.');
347					echo '</div>';
348				} else {
349					// Keep track of the INDI from the parent page, otherwise it will
350					// get lost after ajax updates
351					$pid = Filter::get('pid', WT_REGEX_XREF);
352
353					if ($clip_ctrl->action !== 'download' && $clip_ctrl->action !== 'add') { ?>
354					<form class="wt-page-options wt-page-options-clipping-cart hidden-print" action="module.php">
355						<input type="hidden" name="mod" value="clippings">
356						<input type="hidden" name="mod_action" value="index">
357						<input type="hidden" name="action" value="download">
358						<input type="hidden" name="pid" value="<?= $pid ?>">
359						<table class="add-to center">
360							<tr>
361								<td colspan="2" class="topbottombar">
362									<h2><?= I18N::translate('Download') ?></h2>
363								</td>
364							</tr>
365							<?php if (Auth::isManager($WT_TREE)) { ?>
366								<tr>
367									<td class="descriptionbox width50 wrap">
368										<?= I18N::translate('Apply privacy settings') ?>
369									</td>
370									<td class="optionbox">
371										<input type="radio" name="privatize_export" value="none" checked>
372										<?= I18N::translate('None') ?>
373										<br>
374										<input type="radio" name="privatize_export" value="gedadmin">
375										<?= I18N::translate('Manager') ?>
376										<br>
377										<input type="radio" name="privatize_export" value="user">
378										<?= I18N::translate('Member') ?>
379										<br>
380										<input type="radio" name="privatize_export" value="visitor">
381										<?= I18N::translate('Visitor') ?>
382									</td>
383								</tr>
384							<?php } elseif (Auth::isMember($WT_TREE)) { ?>
385								<tr>
386									<td class="descriptionbox width50 wrap">
387										<?= I18N::translate('Apply privacy settings') ?>
388									</td>
389									<td class="optionbox">
390										<input type="radio" name="privatize_export" value="user" checked> <?= I18N::translate('Member') ?><br>
391										<input type="radio" name="privatize_export" value="visitor"> <?= I18N::translate('Visitor') ?>
392									</td>
393								</tr>
394							<?php } ?>
395
396							<tr>
397								<td class="descriptionbox width50 wrap">
398									<?= I18N::translate('Convert from UTF-8 to ISO-8859-1') ?>
399								</td>
400								<td class="optionbox">
401									<input type="checkbox" name="convert" value="yes">
402								</td>
403							</tr>
404
405							<tr>
406								<td class="topbottombar" colspan="2">
407									<input type="submit" value="<?= /* I18N: A button label. */ I18N::translate('download') ?>">
408								</td>
409							</tr>
410						</table>
411					</form>
412				</div>
413					<br>
414
415					<form class="wt-page-options wt-page-options-clipping-cart hidden-print" name="addin" action="module.php">
416						<input type="hidden" name="mod" value="clippings">
417						<input type="hidden" name="mod_action" value="index">
418						<table class="add-to center">
419							<thead>
420								<tr>
421									<td colspan="2" class="topbottombar" style="text-align:center; ">
422										<?= I18N::translate('Add to the clippings cart') ?>
423									</td>
424								</tr>
425							</thead>
426							<tbody>
427								<tr>
428									<td class="optionbox">
429										<input type="hidden" name="action" value="add">
430										<input type="text" data-autocomplete-type="IFSRO" name="id" id="cart_item_id" size="8">
431									</td>
432									<td class="optionbox">
433										<input type="submit" value="<?= /* I18N: A button label. */ I18N::translate('add') ?>">
434									</td>
435								</tr>
436							</tbody>
437							<tfoot>
438								<tr>
439									<th colspan="2">
440										<a href="module.php?mod=clippings&amp;mod_action=index&amp;action=empty">
441											<?= I18N::translate('Empty the clippings cart') ?>
442										</a>
443									</th>
444								</tr>
445							</tfoot>
446						</table>
447					</form>
448				</div>
449				<?php } ?>
450				<div class="clipping-cart">
451				<h2>
452					<?= I18N::translate('Family tree clippings cart') ?>
453				</h2>
454				<table id="mycart" class="sortable list_table width50">
455					<thead>
456						<tr>
457							<th class="list_label"><?= I18N::translate('Record') ?></th>
458							<th class="list_label"><?= I18N::translate('Remove') ?></th>
459						</tr>
460					</thead>
461					<tbody>
462						<?php
463							foreach (array_keys($cart[$WT_TREE->getTreeId()]) as $xref) {
464								$record = GedcomRecord::getInstance($xref, $WT_TREE);
465								if ($record) {
466									switch ($record::RECORD_TYPE) {
467										case 'INDI':
468											$icon = 'icon-indis';
469											break;
470										case 'FAM':
471											$icon = 'icon-sfamily';
472											break;
473										case 'SOUR':
474											$icon = 'icon-source';
475											break;
476										case 'REPO':
477											$icon = 'icon-repository';
478											break;
479										case 'NOTE':
480											$icon = 'icon-note';
481											break;
482										case 'OBJE':
483											$icon = 'icon-media';
484											break;
485										default:
486											$icon = 'icon-clippings';
487											break;
488									}
489								?>
490								<tr>
491									<td class="list_value">
492										<i class="<?= $icon ?>"></i>
493										<?php
494										echo '<a href="', e($record->url()), '">', $record->getFullName(), '</a>';
495										?>
496									</td>
497									<td class="list_value center vmiddle"><a href="module.php?mod=clippings&amp;mod_action=index&amp;action=remove&amp;id=<?= $xref ?>" class="icon-remove" title="<?= I18N::translate('Remove') ?>"></a></td>
498								</tr>
499								<?php
500							}
501						}
502						?>
503				</table>
504			</div>
505				<?php
506			}
507			break;
508			default:
509				http_response_code(404);
510				break;
511		}
512	}
513
514	/**
515	 * The user can re-order menus. Until they do, they are shown in this order.
516	 *
517	 * @return int
518	 */
519	public function defaultMenuOrder() {
520		return 20;
521	}
522
523	/**
524	 * A menu, to be added to the main application menu.
525	 *
526	 * @return Menu|null
527	 */
528	public function getMenu() {
529		global $controller, $WT_TREE;
530
531		$submenus = [];
532		if (isset($controller->record)) {
533			$submenus[] = new Menu($this->getTitle(), 'module.php?mod=clippings&amp;mod_action=index&amp;ged=' . $WT_TREE->getNameUrl(), 'menu-clippings-cart', ['rel' => 'nofollow']);
534		}
535		if (!empty($controller->record) && $controller->record->canShow()) {
536			$submenus[] = new Menu(I18N::translate('Add to the clippings cart'), 'module.php?mod=clippings&amp;mod_action=index&amp;action=add&amp;id=' . $controller->record->getXref(), 'menu-clippings-add', ['rel' => 'nofollow']);
537		}
538
539		if ($submenus) {
540			return new Menu($this->getTitle(), '#', 'menu-clippings', ['rel' => 'nofollow'], $submenus);
541		} else {
542			return new Menu($this->getTitle(), 'module.php?mod=clippings&amp;mod_action=index&amp;ged=' . $WT_TREE->getNameUrl(), 'menu-clippings', ['rel' => 'nofollow']);
543		}
544	}
545
546	/** {@inheritdoc} */
547	public function defaultSidebarOrder() {
548		return 60;
549	}
550
551	/** {@inheritdoc} */
552	public function hasSidebarContent(Individual $individual) {
553		// Creating a controller has the side effect of initialising the cart
554		new ClippingsCartController;
555
556		return true;
557	}
558
559	/**
560	 * Load this sidebar synchronously.
561	 *
562	 * @param Individual $individual
563	 *
564	 * @return string
565	 */
566	public function getSidebarContent(Individual $individual) {
567		global $controller;
568
569		$controller->addInlineJavascript('
570				$("#sb_clippings_content").on("click", ".add_cart, .remove_cart", function() {
571					$("#sb_clippings_content").load(this.href);
572					return false;
573				});
574			');
575
576		return '<div id="sb_clippings_content">' . $this->getCartList() . '</div>';
577	}
578
579	/** {@inheritdoc} */
580	public function getSidebarAjaxContent() {
581		global $WT_TREE;
582
583		$cart = Session::get('cart');
584
585		$clip_ctrl         = new ClippingsCartController;
586		$add               = Filter::get('add', WT_REGEX_XREF);
587		$add1              = Filter::get('add1', WT_REGEX_XREF);
588		$remove            = Filter::get('remove', WT_REGEX_XREF);
589		$others            = Filter::get('others');
590		$clip_ctrl->level1 = Filter::getInteger('level1');
591		$clip_ctrl->level2 = Filter::getInteger('level2');
592		$clip_ctrl->level3 = Filter::getInteger('level3');
593		if ($add) {
594			$record = GedcomRecord::getInstance($add, $WT_TREE);
595			if ($record) {
596				$clip_ctrl->id   = $record->getXref();
597				$clip_ctrl->type = $record::RECORD_TYPE;
598				$clip_ctrl->addClipping($record);
599			}
600		} elseif ($add1) {
601			$record = Individual::getInstance($add1, $WT_TREE);
602			if ($record) {
603				$clip_ctrl->id   = $record->getXref();
604				$clip_ctrl->type = $record::RECORD_TYPE;
605				if ($others == 'parents') {
606					foreach ($record->getChildFamilies() as $family) {
607						$clip_ctrl->addClipping($family);
608						$clip_ctrl->addFamilyMembers($family);
609					}
610				} elseif ($others == 'ancestors') {
611					$clip_ctrl->addAncestorsToCart($record, $clip_ctrl->level1);
612				} elseif ($others == 'ancestorsfamilies') {
613					$clip_ctrl->addAncestorsToCartFamilies($record, $clip_ctrl->level2);
614				} elseif ($others == 'members') {
615					foreach ($record->getSpouseFamilies() as $family) {
616						$clip_ctrl->addClipping($family);
617						$clip_ctrl->addFamilyMembers($family);
618					}
619				} elseif ($others == 'descendants') {
620					foreach ($record->getSpouseFamilies() as $family) {
621						$clip_ctrl->addClipping($family);
622						$clip_ctrl->addFamilyDescendancy($family, $clip_ctrl->level3);
623					}
624				}
625			}
626		} elseif ($remove) {
627			unset($cart[$WT_TREE->getTreeId()][$remove]);
628			Session::put('cart', $cart);
629		} elseif (isset($_REQUEST['empty'])) {
630			$cart[$WT_TREE->getTreeId()] = [];
631			Session::put('cart', $cart);
632		} elseif (isset($_REQUEST['download'])) {
633			return $this->downloadForm();
634		}
635
636		return $this->getCartList();
637	}
638
639	/**
640	 * A list for the side bar.
641	 *
642	 * @return string
643	 */
644	public function getCartList() {
645		global $WT_TREE;
646
647		$cart = Session::get('cart', []);
648		if (!array_key_exists($WT_TREE->getTreeId(), $cart)) {
649			$cart[$WT_TREE->getTreeId()] = [];
650		}
651		$pid = Filter::get('pid', WT_REGEX_XREF);
652
653		if (!$cart[$WT_TREE->getTreeId()]) {
654			$out = I18N::translate('Your clippings cart is empty.');
655		} else {
656			$out = '';
657			if (!empty($cart[$WT_TREE->getTreeId()])) {
658				$out .=
659					'<a href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;empty=true&amp;pid=' . $pid . '" class="remove_cart">' .
660					I18N::translate('Empty the clippings cart') .
661					'</a>' .
662					'<br>' .
663					'<a href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;download=true&amp;pid=' . $pid . '" class="add_cart">' .
664					I18N::translate('Download') .
665					'</a><br><br>';
666			}
667			$out .= '<ul>';
668			foreach (array_keys($cart[$WT_TREE->getTreeId()]) as $xref) {
669				$record = GedcomRecord::getInstance($xref, $WT_TREE);
670				if ($record instanceof Individual || $record instanceof Family) {
671					switch ($record::RECORD_TYPE) {
672						case 'INDI':
673							$icon = 'icon-indis';
674							break;
675						case 'FAM':
676							$icon = 'icon-sfamily';
677							break;
678					}
679					$out .= '<li>';
680					if (!empty($icon)) {
681						$out .= '<i class="' . $icon . '"></i>';
682					}
683					$out .= '<a href="' . e($record->url()) . '">';
684					if ($record instanceof Individual) {
685						$out .= $record->getSexImage();
686					}
687					$out .= ' ' . $record->getFullName() . ' ';
688					if ($record instanceof Individual && $record->canShow()) {
689						$out .= ' (' . $record->getLifeSpan() . ')';
690					}
691					$out .= '</a>';
692					$out .= '<a class="icon-remove remove_cart" href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;remove=' . $xref . '&amp;pid=' . $pid . '" title="' . I18N::translate('Remove') . '"></a>';
693					$out .= '</li>';
694				}
695			}
696			$out .= '</ul>';
697		}
698
699		$record = Individual::getInstance($pid, $WT_TREE);
700		if ($record && !array_key_exists($record->getXref(), $cart[$WT_TREE->getTreeId()])) {
701			$out .= '<br><a href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;action=add1&amp;type=INDI&amp;id=' . $pid . '&amp;pid=' . $pid . '" class="add_cart"><i class="icon-clippings"></i> ' . I18N::translate('Add %s to the clippings cart', $record->getFullName()) . '</a>';
702		}
703
704		return $out;
705	}
706
707	/**
708	 * A form to choose the download options.
709	 *
710	 * @return string
711	 */
712	public function downloadForm() {
713		global $WT_TREE;
714
715		$pid = Filter::get('pid', WT_REGEX_XREF);
716
717		$out = '<script>';
718		$out .= 'function cancelDownload() {
719				var link = "module.php?mod=' . $this->getName() . '&mod_action=ajax&pid=' . $pid . '";
720				$("#sb_clippings_content").load(link);
721			}';
722		$out .= '</script>';
723		$out .= '<form class="wt-page-options wt-page-options-clipping-cart hidden-print" action="module.php">
724		<input type="hidden" name="mod" value="clippings">
725		<input type="hidden" name="mod_action" value="index">
726		<input type="hidden" name="pid" value="' . $pid . '">
727		<input type="hidden" name="action" value="download">
728		<table>
729		<tr><td colspan="2" class="topbottombar"><h2>' . I18N::translate('Download') . '</h2></td></tr>
730		';
731
732		if (Auth::isManager($WT_TREE)) {
733			$out .=
734				'<tr><td class="descriptionbox width50 wrap">' . I18N::translate('Apply privacy settings') . '</td>' .
735				'<td class="optionbox">' .
736				'<input type="radio" name="privatize_export" value="none" checked> ' . I18N::translate('None') . '<br>' .
737				'<input type="radio" name="privatize_export" value="gedadmin"> ' . I18N::translate('Manager') . '<br>' .
738				'<input type="radio" name="privatize_export" value="user"> ' . I18N::translate('Member') . '<br>' .
739				'<input type="radio" name="privatize_export" value="visitor"> ' . I18N::translate('Visitor') .
740				'</td></tr>';
741		} elseif (Auth::isMember($WT_TREE)) {
742			$out .=
743				'<tr><td class="descriptionbox width50 wrap">' . I18N::translate('Apply privacy settings') . '</td>' .
744				'<td class="list_value">' .
745				'<input type="radio" name="privatize_export" value="user" checked> ' . I18N::translate('Member') . '<br>' .
746				'<input type="radio" name="privatize_export" value="visitor"> ' . I18N::translate('Visitor') .
747				'</td></tr>';
748		}
749
750		$out .= '
751		<tr><td class="descriptionbox width50 wrap">' . I18N::translate('Convert from UTF-8 to ISO-8859-1') . '</td>
752		<td class="optionbox"><input type="checkbox" name="convert" value="yes"></td></tr>
753
754		<tr><td class="topbottombar" colspan="2">
755		<input type="button" class="btn btn-secondary" value="' . /* I18N: A button label. */ I18N::translate('cancel') . '" onclick="cancelDownload();">
756		<input type="submit" class="btn btn-primary" value="' . /* I18N: A button label. */ I18N::translate('download') . '">
757		</form>';
758
759		return $out;
760	}
761}
762