xref: /webtrees/app/Module/CensusAssistantModule.php (revision 6bdf767435631ad1dc27ec1ffd855d43dbdce907)
18c2e8227SGreg Roach<?php
28c2e8227SGreg Roach/**
38c2e8227SGreg Roach * webtrees: online genealogy
4*6bdf7674SGreg Roach * Copyright (C) 2017 webtrees development team
58c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify
68c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by
78c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or
88c2e8227SGreg Roach * (at your option) any later version.
98c2e8227SGreg Roach * This program is distributed in the hope that it will be useful,
108c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
118c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
128c2e8227SGreg Roach * GNU General Public License for more details.
138c2e8227SGreg Roach * You should have received a copy of the GNU General Public License
148c2e8227SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
158c2e8227SGreg Roach */
1676692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module;
1776692c8bSGreg Roach
183a7bc14aSDavid Druryuse Fisharebest\Webtrees\Census\Census;
19ad51e0bbSGreg Roachuse Fisharebest\Webtrees\Census\CensusInterface;
200e62c4b8SGreg Roachuse Fisharebest\Webtrees\Controller\SimpleController;
210e62c4b8SGreg Roachuse Fisharebest\Webtrees\Family;
220e62c4b8SGreg Roachuse Fisharebest\Webtrees\Filter;
23ad51e0bbSGreg Roachuse Fisharebest\Webtrees\Functions\Functions;
243d7a8a4cSGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsDb;
250e62c4b8SGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
260e62c4b8SGreg Roachuse Fisharebest\Webtrees\GedcomTag;
270e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N;
2899f222b3SGreg Roachuse Fisharebest\Webtrees\Individual;
29ad51e0bbSGreg Roachuse Fisharebest\Webtrees\Menu;
300e62c4b8SGreg Roachuse Fisharebest\Webtrees\Note;
31dc46b574SDavid Druryuse Fisharebest\Webtrees\Soundex;
328c2e8227SGreg Roach
338c2e8227SGreg Roach/**
348c2e8227SGreg Roach * Class CensusAssistantModule
358c2e8227SGreg Roach */
3615834aaeSGreg Roachclass CensusAssistantModule extends AbstractModule {
378c2e8227SGreg Roach	/** {@inheritdoc} */
388c2e8227SGreg Roach	public function getTitle() {
398c2e8227SGreg Roach		return /* I18N: Name of a module */ I18N::translate('Census assistant');
408c2e8227SGreg Roach	}
418c2e8227SGreg Roach
428c2e8227SGreg Roach	/** {@inheritdoc} */
438c2e8227SGreg Roach	public function getDescription() {
448c2e8227SGreg Roach		return /* I18N: Description of the “Census assistant” module */ I18N::translate('An alternative way to enter census transcripts and link them to individuals.');
458c2e8227SGreg Roach	}
468c2e8227SGreg Roach
4776692c8bSGreg Roach	/**
4876692c8bSGreg Roach	 * This is a general purpose hook, allowing modules to respond to routes
4976692c8bSGreg Roach	 * of the form module.php?mod=FOO&mod_action=BAR
5076692c8bSGreg Roach	 *
5176692c8bSGreg Roach	 * @param string $mod_action
5276692c8bSGreg Roach	 */
538c2e8227SGreg Roach	public function modAction($mod_action) {
548c2e8227SGreg Roach		switch ($mod_action) {
5540990b78SGreg Roach		case 'census_find':
5640990b78SGreg Roach			self::censusFind();
578c2e8227SGreg Roach			break;
5840990b78SGreg Roach		case 'media_find':
59764a01d9SGreg Roach			self::mediaFind();
608c2e8227SGreg Roach			break;
618c2e8227SGreg Roach		case 'media_query_3a':
62764a01d9SGreg Roach			self::mediaQuery();
638c2e8227SGreg Roach			break;
648c2e8227SGreg Roach		default:
658c2e8227SGreg Roach			http_response_code(404);
668c2e8227SGreg Roach		}
678c2e8227SGreg Roach	}
688c2e8227SGreg Roach
698c2e8227SGreg Roach	/**
7076692c8bSGreg Roach	 * Find an individual.
718c2e8227SGreg Roach	 */
7240990b78SGreg Roach	private static function censusFind() {
7340990b78SGreg Roach		global $WT_TREE;
7440990b78SGreg Roach
7540990b78SGreg Roach		$controller = new SimpleController;
7640990b78SGreg Roach		$filter     = Filter::get('filter');
7740990b78SGreg Roach		$action     = Filter::get('action');
78ad51e0bbSGreg Roach		$census     = Filter::get('census');
79ad51e0bbSGreg Roach		$census     = new $census;
8040990b78SGreg Roach
8140990b78SGreg Roach		$controller
82ad51e0bbSGreg Roach			->restrictAccess($census instanceof CensusInterface)
8340990b78SGreg Roach			->setPageTitle(I18N::translate('Find an individual'))
8440990b78SGreg Roach			->pageHeader();
8540990b78SGreg Roach
86ad51e0bbSGreg Roach		echo '<table class="list_table width90" border="0">';
87a86dd8b1SGreg Roach		echo '<tr><td style="padding: 10px;" class="facts_label03 width90">';
8840990b78SGreg Roach		echo I18N::translate('Find an individual');
89ad51e0bbSGreg Roach		echo '</td>';
90ad51e0bbSGreg Roach		echo '</table>';
91ad51e0bbSGreg Roach		echo '<br>';
9240990b78SGreg Roach
93ad51e0bbSGreg Roach		if ($action == 'filter') {
9440990b78SGreg Roach			$filter       = trim($filter);
9540990b78SGreg Roach			$filter_array = explode(' ', preg_replace('/ {2,}/', ' ', $filter));
9640990b78SGreg Roach
9740990b78SGreg Roach			// Output Individual for GEDFact Assistant ======================
98ad51e0bbSGreg Roach			echo '<table class="list_table width90">';
9913abd6f3SGreg Roach			$myindilist = FunctionsDb::searchIndividualNames($filter_array, [$WT_TREE]);
10040990b78SGreg Roach			if ($myindilist) {
101ad51e0bbSGreg Roach				echo '<tr><td class="list_value_wrap"><ul>';
10240990b78SGreg Roach				usort($myindilist, '\Fisharebest\Webtrees\GedcomRecord::compare');
10340990b78SGreg Roach				foreach ($myindilist as $indi) {
104ad51e0bbSGreg Roach					echo '<li>';
1050b18a98dSGreg Roach					echo '<a href="#" onclick="window.opener.appendCensusRow(\'' . Filter::escapeJs(self::censusTableRow($census, $indi, null)) . '\'); window.close();">';
106ad51e0bbSGreg Roach					echo '<b>' . $indi->getFullName() . '</b>';
107ad51e0bbSGreg Roach					echo '</a>';
108ad51e0bbSGreg Roach					echo $indi->formatFirstMajorFact(WT_EVENTS_BIRT, 1);
109ad51e0bbSGreg Roach					echo $indi->formatFirstMajorFact(WT_EVENTS_DEAT, 1);
110ad51e0bbSGreg Roach					echo '<hr>';
111ad51e0bbSGreg Roach					echo '</li>';
11240990b78SGreg Roach				}
11340990b78SGreg Roach				echo '</ul></td></tr>';
11440990b78SGreg Roach			} else {
115ad51e0bbSGreg Roach				echo '<tr><td class="list_value_wrap">';
11640990b78SGreg Roach				echo I18N::translate('No results found.');
117ad51e0bbSGreg Roach				echo '</td></tr>';
11840990b78SGreg Roach			}
119ad51e0bbSGreg Roach			echo '<tr><td>';
12040990b78SGreg Roach			echo '<button onclick="window.close();">', I18N::translate('close'), '</button>';
121ad51e0bbSGreg Roach			echo '</td></tr>';
122ad51e0bbSGreg Roach			echo '</table>';
123ad51e0bbSGreg Roach		}
12440990b78SGreg Roach	}
12540990b78SGreg Roach
12640990b78SGreg Roach	/**
12776692c8bSGreg Roach	 * Find a media object.
12840990b78SGreg Roach	 */
129764a01d9SGreg Roach	private static function mediaFind() {
1308c2e8227SGreg Roach		global $WT_TREE;
1318c2e8227SGreg Roach
1328c2e8227SGreg Roach		$controller = new SimpleController;
1338c2e8227SGreg Roach		$filter     = Filter::get('filter');
1348c2e8227SGreg Roach		$multiple   = Filter::getBool('multiple');
1358c2e8227SGreg Roach
1368c2e8227SGreg Roach		$controller
1378c2e8227SGreg Roach			->setPageTitle(I18N::translate('Find an individual'))
1388c2e8227SGreg Roach			->pageHeader();
1398c2e8227SGreg Roach
1408c2e8227SGreg Roach		?>
14199f222b3SGreg Roach		<script>
1428c2e8227SGreg Roach		function pasterow(id, name, gend, yob, age, bpl) {
1438c2e8227SGreg Roach			window.opener.opener.insertRowToTable(id, name, '', gend, '', yob, age, 'Y', '', bpl);
1448c2e8227SGreg Roach		}
1458c2e8227SGreg Roach
1468c2e8227SGreg Roach		function pasteid(id, name, thumb) {
1478c2e8227SGreg Roach			if (thumb) {
1488c2e8227SGreg Roach				window.opener.paste_id(id, name, thumb);
1497a6ee1acSGreg Roach				<?php if (!$multiple) { echo 'window.close();'; } ?>
1508c2e8227SGreg Roach			} else {
1518c2e8227SGreg Roach			// GEDFact_assistant ========================
1528c2e8227SGreg Roach			if (window.opener.document.getElementById('addlinkQueue')) {
1538c2e8227SGreg Roach				window.opener.insertRowToTable(id, name);
1548c2e8227SGreg Roach			}
1558c2e8227SGreg Roach			window.opener.paste_id(id);
1568c2e8227SGreg Roach			if (window.opener.pastename) {
1578c2e8227SGreg Roach				window.opener.pastename(name);
1588c2e8227SGreg Roach			}
1597a6ee1acSGreg Roach			<?php if (!$multiple) { echo 'window.close();'; } ?>
1608c2e8227SGreg Roach			}
1618c2e8227SGreg Roach		}
1628c2e8227SGreg Roach		function checknames(frm) {
1638c2e8227SGreg Roach			if (document.forms[0].subclick) {
1648c2e8227SGreg Roach				button = document.forms[0].subclick.value;
1658c2e8227SGreg Roach			} else {
1668c2e8227SGreg Roach				button = "";
1678c2e8227SGreg Roach			}
168ad51e0bbSGreg Roach			if (frm.filter.value.length < 2 && button !== "all") {
169564ae2d7SGreg Roach				alert("<?= I18N::translate('Please enter more than one character.') ?>");
1708c2e8227SGreg Roach				frm.filter.focus();
1718c2e8227SGreg Roach				return false;
1728c2e8227SGreg Roach			}
1738c2e8227SGreg Roach			if (button=="all") {
1748c2e8227SGreg Roach				frm.filter.value = "";
1758c2e8227SGreg Roach			}
1768c2e8227SGreg Roach			return true;
1778c2e8227SGreg Roach		}
17899f222b3SGreg Roach		</script>
1798c2e8227SGreg Roach
18099f222b3SGreg Roach		<?php
181a86dd8b1SGreg Roach		echo '<div>';
1824c621133SGreg Roach		echo '<table class="list_table width90" border="0">';
183a86dd8b1SGreg Roach		echo '<tr><td style="padding: 10px;" class="facts_label03 width90">'; // start column for find text header
1848c2e8227SGreg Roach		echo $controller->getPageTitle();
1854c621133SGreg Roach		echo '</td>';
1864c621133SGreg Roach		echo '</tr>';
1874c621133SGreg Roach		echo '</table>';
1884c621133SGreg Roach		echo '<br>';
1898c2e8227SGreg Roach		echo '<button onclick="window.close();">', I18N::translate('close'), '</button>';
1904c621133SGreg Roach		echo '<br>';
1918c2e8227SGreg Roach
1928c2e8227SGreg Roach		$filter       = trim($filter);
1938c2e8227SGreg Roach		$filter_array = explode(' ', preg_replace('/ {2,}/', ' ', $filter));
1944c621133SGreg Roach		echo '<table class="tabs_table width90"><tr>';
19513abd6f3SGreg Roach		$myindilist = FunctionsDb::searchIndividualNames($filter_array, [$WT_TREE]);
1968c2e8227SGreg Roach		if ($myindilist) {
1974c621133SGreg Roach			echo '<td class="list_value_wrap"><ul>';
1980e62c4b8SGreg Roach			usort($myindilist, '\Fisharebest\Webtrees\GedcomRecord::compare');
1998c2e8227SGreg Roach			foreach ($myindilist as $indi) {
2008c2e8227SGreg Roach				$nam = Filter::escapeHtml($indi->getFullName());
2018c2e8227SGreg Roach				echo "<li><a href=\"#\" onclick=\"pasterow(
2028c2e8227SGreg Roach					'" . $indi->getXref() . "' ,
2038c2e8227SGreg Roach					'" . $nam . "' ,
2048c2e8227SGreg Roach					'" . $indi->getSex() . "' ,
2057820e4d7SGreg Roach					'" . $indi->getBirthYear() . "' ,
2067820e4d7SGreg Roach					'" . (1901 - $indi->getBirthYear()) . "' ,
2077820e4d7SGreg Roach					'" . $indi->getBirthPlace() . "'); return false;\">
2087a6ee1acSGreg Roach					<b>" . $indi->getFullName() . '</b>&nbsp;&nbsp;&nbsp;';
2098c2e8227SGreg Roach
210564ae2d7SGreg Roach				$born = I18N::translate('Birth');
2117a6ee1acSGreg Roach				echo '</span><br><span class="list_item">', $born, ' ', $indi->getBirthYear(), '&nbsp;&nbsp;&nbsp;', $indi->getBirthPlace(), '</span></a></li>';
2127a6ee1acSGreg Roach				echo '<hr>';
2138c2e8227SGreg Roach			}
2148c2e8227SGreg Roach			echo '</ul></td></tr><tr><td class="list_label">', I18N::translate('Total individuals: %s', count($myindilist)), '</tr></td>';
2158c2e8227SGreg Roach		} else {
2167a6ee1acSGreg Roach			echo '<td class="list_value_wrap">';
2178c2e8227SGreg Roach			echo I18N::translate('No results found.');
2187a6ee1acSGreg Roach			echo '</td></tr>';
2198c2e8227SGreg Roach		}
2207a6ee1acSGreg Roach		echo '</table>';
2218c2e8227SGreg Roach		echo '</div>';
2228c2e8227SGreg Roach	}
2238c2e8227SGreg Roach
2248c2e8227SGreg Roach	/**
22576692c8bSGreg Roach	 * Search for a media object.
2268c2e8227SGreg Roach	 */
227764a01d9SGreg Roach	private static function mediaQuery() {
22824ec66ceSGreg Roach		global $WT_TREE;
22924ec66ceSGreg Roach
2308c2e8227SGreg Roach		$iid2 = Filter::get('iid', WT_REGEX_XREF);
2318c2e8227SGreg Roach
2328c2e8227SGreg Roach		$controller = new SimpleController;
2338c2e8227SGreg Roach		$controller
2348c2e8227SGreg Roach			->setPageTitle(I18N::translate('Link to an existing media object'))
2358c2e8227SGreg Roach			->pageHeader();
2368c2e8227SGreg Roach
23724ec66ceSGreg Roach		$record = GedcomRecord::getInstance($iid2, $WT_TREE);
2388c2e8227SGreg Roach		if ($record) {
2398c2e8227SGreg Roach			$headjs = '';
2408c2e8227SGreg Roach			if ($record instanceof Family) {
2418c2e8227SGreg Roach				if ($record->getHusband()) {
2428c2e8227SGreg Roach					$headjs = $record->getHusband()->getXref();
2438c2e8227SGreg Roach				} elseif ($record->getWife()) {
2448c2e8227SGreg Roach					$headjs = $record->getWife()->getXref();
2458c2e8227SGreg Roach				}
2468c2e8227SGreg Roach			}
2478c2e8227SGreg Roach			?>
2488c2e8227SGreg Roach			<script>
2498c2e8227SGreg Roach				function insertId() {
2508c2e8227SGreg Roach					if (window.opener.document.getElementById('addlinkQueue')) {
2518c2e8227SGreg Roach						// alert('Please move this alert window and examine the contents of the pop-up window, then click OK')
252564ae2d7SGreg Roach						window.opener.insertRowToTable('<?= $record->getXref() ?>', '<?= htmlspecialchars($record->getFullName()) ?>', '<?= $headjs ?>');
2538c2e8227SGreg Roach						window.close();
2548c2e8227SGreg Roach					}
2558c2e8227SGreg Roach				}
2568c2e8227SGreg Roach			</script>
2578c2e8227SGreg Roach			<?php
2588c2e8227SGreg Roach		} else {
2598c2e8227SGreg Roach			?>
2608c2e8227SGreg Roach			<script>
2618c2e8227SGreg Roach				function insertId() {
262564ae2d7SGreg Roach					window.opener.alert('<?= $iid2 ?> - <?= I18N::translate('Not a valid individual, family, or source ID') ?>');
2638c2e8227SGreg Roach					window.close();
2648c2e8227SGreg Roach				}
2658c2e8227SGreg Roach			</script>
2668c2e8227SGreg Roach			<?php
2678c2e8227SGreg Roach		}
2688c2e8227SGreg Roach		?>
2698c2e8227SGreg Roach		<script>window.onLoad = insertId();</script>
2708c2e8227SGreg Roach		<?php
2718c2e8227SGreg Roach	}
2728c2e8227SGreg Roach
2738c2e8227SGreg Roach	/**
2748c2e8227SGreg Roach	 * Convert custom markup into HTML
2758c2e8227SGreg Roach	 *
2768c2e8227SGreg Roach	 * @param Note $note
2778c2e8227SGreg Roach	 *
2788c2e8227SGreg Roach	 * @return string
2798c2e8227SGreg Roach	 */
2808c2e8227SGreg Roach	public static function formatCensusNote(Note $note) {
2818c2e8227SGreg Roach		global $WT_TREE;
2828c2e8227SGreg Roach
28314c3d3fbSGreg Roach		if (preg_match('/(.*)((?:\n.*)*)\n\.start_formatted_area\.\n(.+)\n(.+(?:\n.+)*)\n.end_formatted_area\.((?:\n.*)*)/', $note->getNote(), $match)) {
2848c2e8227SGreg Roach			// This looks like a census-assistant shared note
2858c2e8227SGreg Roach			$title     = Filter::escapeHtml($match[1]);
2868c2e8227SGreg Roach			$preamble  = Filter::escapeHtml($match[2]);
2878c2e8227SGreg Roach			$header    = Filter::escapeHtml($match[3]);
2888c2e8227SGreg Roach			$data      = Filter::escapeHtml($match[4]);
2898c2e8227SGreg Roach			$postamble = Filter::escapeHtml($match[5]);
2908c2e8227SGreg Roach
291dc46b574SDavid Drury			// Get the column headers for the census to which this note refers
292dc46b574SDavid Drury			// requires the fact place & date to match the specific census
293dc46b574SDavid Drury			// censusPlace() (Soundex match) and censusDate() functions
29413abd6f3SGreg Roach			$fmt_headers   = [];
295dc46b574SDavid Drury			$linkedRecords = array_merge($note->linkedIndividuals('NOTE'), $note->linkedFamilies('NOTE'));
296dc46b574SDavid Drury			$firstRecord   = array_shift($linkedRecords);
297dc46b574SDavid Drury			if ($firstRecord) {
298dc46b574SDavid Drury				$countryCode = '';
299dc46b574SDavid Drury				$date        = '';
300dc46b574SDavid Drury				foreach ($firstRecord->getFacts('CENS') as $fact) {
301dc46b574SDavid Drury					if (trim($fact->getAttribute('NOTE'), '@') === $note->getXref()) {
302dc46b574SDavid Drury						$date        = $fact->getAttribute('DATE');
303dc46b574SDavid Drury						$place       = explode(',', strip_tags($fact->getPlace()->getFullName()));
304dc46b574SDavid Drury						$countryCode = Soundex::daitchMokotoff(array_pop($place));
305dc46b574SDavid Drury						break;
306dc46b574SDavid Drury					}
3078c2e8227SGreg Roach				}
3088c2e8227SGreg Roach
309dc46b574SDavid Drury				foreach (Census::allCensusPlaces() as $censusPlace) {
310dc46b574SDavid Drury					if (Soundex::compare($countryCode, Soundex::daitchMokotoff($censusPlace->censusPlace()))) {
311dc46b574SDavid Drury						foreach ($censusPlace->allCensusDates() as $census) {
312dc46b574SDavid Drury							if ($census->censusDate() == $date) {
313dc46b574SDavid Drury								foreach ($census->columns() as $column) {
314dc46b574SDavid Drury									$abbrev = $column->abbreviation();
315dc46b574SDavid Drury									if ($abbrev) {
316dc46b574SDavid Drury										$description          = $column->title() ? $column->title() : I18N::translate('Description unavailable');
317dc46b574SDavid Drury										$fmt_headers[$abbrev] = '<span title="' . $description . '">' . $abbrev . '</span>';
318dc46b574SDavid Drury									}
319dc46b574SDavid Drury								}
320dc46b574SDavid Drury								break 2;
321dc46b574SDavid Drury							}
322dc46b574SDavid Drury						}
323dc46b574SDavid Drury					}
324dc46b574SDavid Drury				}
325dc46b574SDavid Drury			}
326dc46b574SDavid Drury			// Substitute header labels and format as HTML
3278c2e8227SGreg Roach			$thead = '<tr><th>' . strtr(str_replace('|', '</th><th>', $header), $fmt_headers) . '</th></tr>';
328ad51e0bbSGreg Roach			$thead = str_replace('.b.', '', $thead);
3298c2e8227SGreg Roach
3308c2e8227SGreg Roach			// Format data as HTML
3318c2e8227SGreg Roach			$tbody = '';
33214c3d3fbSGreg Roach			foreach (explode("\n", $data) as $row) {
3338c2e8227SGreg Roach				$tbody .= '<tr>';
3348c2e8227SGreg Roach				foreach (explode('|', $row) as $column) {
3358c2e8227SGreg Roach					$tbody .= '<td>' . $column . '</td>';
3368c2e8227SGreg Roach				}
3378c2e8227SGreg Roach				$tbody .= '</tr>';
3388c2e8227SGreg Roach			}
3398c2e8227SGreg Roach
3408c2e8227SGreg Roach			return
3418c2e8227SGreg Roach				$title . "\n" . // The newline allows the framework to expand the details and turn the first line into a link
342673157b0SGreg Roach				'<div class="markdown">' .
3438c2e8227SGreg Roach				'<p>' . $preamble . '</p>' .
344dc46b574SDavid Drury				'<table>' .
3458c2e8227SGreg Roach				'<thead>' . $thead . '</thead>' .
3468c2e8227SGreg Roach				'<tbody>' . $tbody . '</tbody>' .
3478c2e8227SGreg Roach				'</table>' .
348dc46b574SDavid Drury				'<p>' . $postamble . '</p>' .
349dc46b574SDavid Drury				'</div>';
3508c2e8227SGreg Roach		} else {
3518c2e8227SGreg Roach			// Not a census-assistant shared note - apply default formatting
3528c2e8227SGreg Roach			return Filter::formatText($note->getNote(), $WT_TREE);
3538c2e8227SGreg Roach		}
3548c2e8227SGreg Roach	}
35599f222b3SGreg Roach
35699f222b3SGreg Roach	/**
357ad51e0bbSGreg Roach	 * Generate an HTML row of data for the census header
35899f222b3SGreg Roach	 *
35952bc9faeSGreg Roach	 * Add prefix cell (store XREF and drag/drop)
36052bc9faeSGreg Roach	 * Add suffix cell (delete button)
36152bc9faeSGreg Roach	 *
362ad51e0bbSGreg Roach	 * @param CensusInterface $census
36399f222b3SGreg Roach	 *
364ad51e0bbSGreg Roach	 * @return string
36599f222b3SGreg Roach	 */
366ad51e0bbSGreg Roach	public static function censusTableHeader(CensusInterface $census) {
36752bc9faeSGreg Roach		$html = '';
368ad51e0bbSGreg Roach		foreach ($census->columns() as $column) {
369ad51e0bbSGreg Roach			$html .= '<th title="' . $column->title() . '">' . $column->abbreviation() . '</th>';
37099f222b3SGreg Roach		}
37199f222b3SGreg Roach
372dc46b574SDavid Drury		return '<tr><th hidden></th>' . $html . '<th></th></tr>';
373ad51e0bbSGreg Roach	}
37499f222b3SGreg Roach
375ad51e0bbSGreg Roach	/**
376ad51e0bbSGreg Roach	 * Generate an HTML row of data for the census
377ad51e0bbSGreg Roach	 *
37852bc9faeSGreg Roach	 * Add prefix cell (store XREF and drag/drop)
37952bc9faeSGreg Roach	 * Add suffix cell (delete button)
38052bc9faeSGreg Roach	 *
381ad51e0bbSGreg Roach	 * @param CensusInterface $census
382ad51e0bbSGreg Roach	 *
383ad51e0bbSGreg Roach	 * @return string
384ad51e0bbSGreg Roach	 */
385ad51e0bbSGreg Roach	public static function censusTableEmptyRow(CensusInterface $census) {
38652bc9faeSGreg Roach		return '<tr><td hidden></td>' . str_repeat('<td><input type="text"></td>', count($census->columns())) . '<td><a class="icon-remove" href="#" title="' . I18N::translate('Remove') . '"></a></td></tr>';
387ad51e0bbSGreg Roach	}
38899f222b3SGreg Roach
389ad51e0bbSGreg Roach	/**
390ad51e0bbSGreg Roach	 * Generate an HTML row of data for the census
391ad51e0bbSGreg Roach	 *
39252bc9faeSGreg Roach	 * Add prefix cell (store XREF and drag/drop)
39352bc9faeSGreg Roach	 * Add suffix cell (delete button)
39452bc9faeSGreg Roach	 *
395ad51e0bbSGreg Roach	 * @param CensusInterface $census
396ad51e0bbSGreg Roach	 * @param Individual      $individual
397ad51e0bbSGreg Roach	 * @param Individual|null $head
398ad51e0bbSGreg Roach	 *
399ad51e0bbSGreg Roach	 * @return string
400ad51e0bbSGreg Roach	 */
401ad51e0bbSGreg Roach	public static function censusTableRow(CensusInterface $census, Individual $individual, Individual $head = null) {
40252bc9faeSGreg Roach		$html = '';
403ad51e0bbSGreg Roach		foreach ($census->columns() as $column) {
404ad51e0bbSGreg Roach			$html .= '<td><input type="text" value="' . $column->generate($individual, $head) . '"></td>';
405ad51e0bbSGreg Roach		}
406ad51e0bbSGreg Roach
40752bc9faeSGreg Roach		return '<tr><td hidden>' . $individual->getXref() . '</td>' . $html . '<td><a class="icon-remove" href="#" title="' . I18N::translate('Remove') . '"></a></td></tr>';
408ad51e0bbSGreg Roach	}
409ad51e0bbSGreg Roach
410ad51e0bbSGreg Roach	/**
411ad51e0bbSGreg Roach	 * Create a family on the census navigator.
412ad51e0bbSGreg Roach	 *
413ad51e0bbSGreg Roach	 * @param CensusInterface $census
414ad51e0bbSGreg Roach	 * @param Family          $family
415ad51e0bbSGreg Roach	 * @param Individual      $head
416ad51e0bbSGreg Roach	 *
417ad51e0bbSGreg Roach	 * @return string
418ad51e0bbSGreg Roach	 */
419ad51e0bbSGreg Roach	public static function censusNavigatorFamily(CensusInterface $census, Family $family, Individual $head) {
4202a277e58SGreg Roach		$headImg2 = '<i class="icon-button_head" title="' . I18N::translate('Head of household') . '"></i>';
421ad51e0bbSGreg Roach
422ad51e0bbSGreg Roach		foreach ($family->getSpouses() as $spouse) {
423ad51e0bbSGreg Roach			$menu = new Menu(Functions::getCloseRelationshipName($head, $spouse));
424ad51e0bbSGreg Roach			foreach ($spouse->getChildFamilies() as $grandparents) {
425ad51e0bbSGreg Roach				foreach ($grandparents->getSpouses() as $grandparent) {
426ad51e0bbSGreg Roach					$submenu = new Menu(
427ad51e0bbSGreg Roach						Functions::getCloseRelationshipName($head, $grandparent) . ' - ' . $grandparent->getFullName(),
428ad51e0bbSGreg Roach						'#',
429ad51e0bbSGreg Roach						'',
43013abd6f3SGreg Roach						['onclick' => 'return appendCensusRow("' . Filter::escapeJs(self::censusTableRow($census, $grandparent, $head)) . '");']
431ad51e0bbSGreg Roach					);
432ad51e0bbSGreg Roach					$submenu->addClass('submenuitem', '');
433ad51e0bbSGreg Roach					$menu->addSubmenu($submenu);
434ad51e0bbSGreg Roach					$menu->addClass('', 'submenu');
43599f222b3SGreg Roach				}
43699f222b3SGreg Roach			}
43799f222b3SGreg Roach
438ad51e0bbSGreg Roach			?>
439ad51e0bbSGreg Roach			<tr>
440ad51e0bbSGreg Roach				<td class="optionbox">
441564ae2d7SGreg Roach					<?= $menu->getMenu() ?>
442ad51e0bbSGreg Roach				</td>
443ad51e0bbSGreg Roach				<td class="facts_value nowrap">
444564ae2d7SGreg Roach					<a href="#" onclick="return appendCensusRow('<?= Filter::escapeJs(self::censusTableRow($census, $spouse, $head)) ?>');">
445564ae2d7SGreg Roach						<?= $spouse->getFullName() ?>
446ad51e0bbSGreg Roach					</a>
447ad51e0bbSGreg Roach				</td>
448a86dd8b1SGreg Roach				<td class="facts_value">
4492a277e58SGreg Roach					<?php if ($head !== $spouse): ?>
450564ae2d7SGreg Roach						<a href="edit_interface.php?action=addnewnote_assisted&amp;noteid=newnote&amp;xref=<?= $spouse->getXref() ?>&amp;gedcom=<?= $spouse->getTree()->getNameUrl() ?>&amp;census=<?= get_class($census) ?>">
451564ae2d7SGreg Roach							<?= $headImg2 ?>
452ad51e0bbSGreg Roach						</a>
453564ae2d7SGreg Roach					<?php endif ?>
454ad51e0bbSGreg Roach				</td>
455ad51e0bbSGreg Roach			</tr>
456ad51e0bbSGreg Roach			<?php
45799f222b3SGreg Roach		}
458ad51e0bbSGreg Roach
459ad51e0bbSGreg Roach		foreach ($family->getChildren() as $child) {
460ad51e0bbSGreg Roach			$menu = new Menu(Functions::getCloseRelationshipName($head, $child));
461ad51e0bbSGreg Roach			foreach ($child->getSpouseFamilies() as $spouse_family) {
462ad51e0bbSGreg Roach				foreach ($spouse_family->getSpouses() as $spouse_family_spouse) {
463ad51e0bbSGreg Roach					if ($spouse_family_spouse != $child) {
464ad51e0bbSGreg Roach						$submenu = new Menu(
465ad51e0bbSGreg Roach							Functions::getCloseRelationshipName($head, $spouse_family_spouse) . ' - ' . $spouse_family_spouse->getFullName(),
466ad51e0bbSGreg Roach							'#',
467ad51e0bbSGreg Roach							'',
46813abd6f3SGreg Roach							['onclick' => 'return appendCensusRow("' . Filter::escapeJs(self::censusTableRow($census, $spouse_family_spouse, $head)) . '");']
469ad51e0bbSGreg Roach						);
470ad51e0bbSGreg Roach						$submenu->addClass('submenuitem', '');
471ad51e0bbSGreg Roach						$menu->addSubmenu($submenu);
472ad51e0bbSGreg Roach						$menu->addClass('', 'submenu');
473ad51e0bbSGreg Roach					}
474ad51e0bbSGreg Roach				}
475ad51e0bbSGreg Roach				foreach ($spouse_family->getChildren() as $spouse_family_child) {
476ad51e0bbSGreg Roach					$submenu = new Menu(
477ad51e0bbSGreg Roach						Functions::getCloseRelationshipName($head, $spouse_family_child) . ' - ' . $spouse_family_child->getFullName(),
478ad51e0bbSGreg Roach						'#',
479ad51e0bbSGreg Roach						'',
48013abd6f3SGreg Roach						['onclick' => 'return appendCensusRow("' . Filter::escapeJs(self::censusTableRow($census, $spouse_family_child, $head)) . '");']
481ad51e0bbSGreg Roach					);
482ad51e0bbSGreg Roach					$submenu->addClass('submenuitem', '');
483ad51e0bbSGreg Roach					$menu->addSubmenu($submenu);
484ad51e0bbSGreg Roach					$menu->addClass('', 'submenu');
48599f222b3SGreg Roach				}
48699f222b3SGreg Roach			}
48799f222b3SGreg Roach
488ad51e0bbSGreg Roach			?>
489ad51e0bbSGreg Roach			<tr>
490ad51e0bbSGreg Roach				<td class="optionbox">
491564ae2d7SGreg Roach					<?= $menu->getMenu() ?>
492ad51e0bbSGreg Roach				</td>
493ad51e0bbSGreg Roach				<td class="facts_value">
494564ae2d7SGreg Roach					<a href="#" onclick="return appendCensusRow('<?= Filter::escapeJs(self::censusTableRow($census, $child, $head)) ?>');">
495564ae2d7SGreg Roach						<?= $child->getFullName() ?>
496ad51e0bbSGreg Roach					</a>
497ad51e0bbSGreg Roach				</td>
498ad51e0bbSGreg Roach				<td class="facts_value">
4992a277e58SGreg Roach					<?php if ($head !== $child): ?>
500564ae2d7SGreg Roach						<a href="edit_interface.php?action=addnewnote_assisted&amp;noteid=newnote&amp;xref=<?= $child->getXref() ?>&amp;gedcom=<?= $child->getTree()->getNameUrl() ?>&amp;census=<?= get_class($census) ?>">
501564ae2d7SGreg Roach							<?= $headImg2 ?>
502ad51e0bbSGreg Roach						</a>
503564ae2d7SGreg Roach					<?php endif ?>
504ad51e0bbSGreg Roach				</td>
505ad51e0bbSGreg Roach			</tr>
506ad51e0bbSGreg Roach			<?php
50799f222b3SGreg Roach		}
508ad51e0bbSGreg Roach		echo '<tr><td><br></td></tr>';
50999f222b3SGreg Roach	}
5108c2e8227SGreg Roach}
511