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