xref: /webtrees/app/Module/CensusAssistantModule.php (revision 8c448b1174350910446db269a6802ffac50a6a78)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2017 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 */
16
17namespace Fisharebest\Webtrees\Module;
18
19use Fisharebest\Webtrees\Census\Census;
20use Fisharebest\Webtrees\Census\CensusInterface;
21use Fisharebest\Webtrees\Family;
22use Fisharebest\Webtrees\Filter;
23use Fisharebest\Webtrees\FontAwesome;
24use Fisharebest\Webtrees\Functions\FunctionsDb;
25use Fisharebest\Webtrees\Functions\FunctionsEdit;
26use Fisharebest\Webtrees\GedcomRecord;
27use Fisharebest\Webtrees\Html;
28use Fisharebest\Webtrees\I18N;
29use Fisharebest\Webtrees\Individual;
30use Fisharebest\Webtrees\Note;
31use Fisharebest\Webtrees\Soundex;
32use Fisharebest\Webtrees\View;
33
34/**
35 * Class CensusAssistantModule
36 */
37class CensusAssistantModule extends AbstractModule {
38	/** {@inheritdoc} */
39	public function getTitle() {
40		return /* I18N: Name of a module */
41			I18N::translate('Census assistant');
42	}
43
44	/** {@inheritdoc} */
45	public function getDescription() {
46		return /* I18N: Description of the “Census assistant” module */
47			I18N::translate('An alternative way to enter census transcripts and link them to individuals.');
48	}
49
50	/**
51	 * This is a general purpose hook, allowing modules to respond to routes
52	 * of the form module.php?mod=FOO&mod_action=BAR
53	 *
54	 * @param string $mod_action
55	 */
56	public function modAction($mod_action) {
57		global $WT_TREE;
58
59		switch ($mod_action) {
60			case 'census-header':
61				header('Content-Type: text/html; charset=utf8');
62				$census = Filter::get('census');
63				echo $this->censusTableHeader(new $census);
64				break;
65
66			case 'census-individual':
67				header('Content-Type: text/html; charset=utf8');
68				$census     = Filter::get('census');
69				$individual = Individual::getInstance(Filter::get('xref'), $WT_TREE);
70				$head       = Individual::getInstance(Filter::get('head'), $WT_TREE);
71				echo $this->censusTableRow(new $census, $individual, $head);
72				break;
73
74			case 'media_find':
75				self::mediaFind();
76				break;
77			case 'media_query_3a':
78				self::mediaQuery();
79				break;
80			default:
81				http_response_code(404);
82		}
83	}
84
85	/**
86	 * @param Individual $individual
87	 */
88	public function createCensusAssistant(Individual $individual) {
89		return View::make('modules/census-assistant', [
90			'individual' => $individual,
91		]);
92	}
93
94	/**
95	 * @param Individual $individual
96	 * @param string     $fact_id
97	 * @param string     $newged
98	 * @param bool       $keep_chan
99	 *
100	 * @return string
101	 */
102	public function updateCensusAssistant(Individual $individual, $fact_id, $newged, $keep_chan) {
103		$ca_title       = Filter::post('ca_title');
104		$ca_place       = Filter::post('ca_place');
105		$ca_citation    = Filter::post('ca_citation');
106		$ca_individuals = Filter::postArray('ca_individuals');
107		$ca_notes       = Filter::post('ca_notes');
108		$ca_census      = Filter::post('ca_census', 'Fisharebest\\\\Webtrees\\\\Census\\\\CensusOf[A-Za-z0-9]+');
109
110		if ($ca_census !== '' && !empty($ca_individuals)) {
111			$census = new $ca_census;
112
113			$note_text   = $this->createNoteText($census, $ca_title, $ca_place, $ca_citation, $ca_individuals, $ca_notes);
114			$note_gedcom = '0 @new@ NOTE ' . str_replace("\n", "\n1 CONT ", $note_text);
115			$note        = $individual->getTree()->createRecord($note_gedcom);
116
117			$newged .= "\n2 NOTE @" . $note->getXref() . '@';
118
119			// Add the census fact to the rest of the household
120			foreach (array_keys($ca_individuals) as $xref) {
121				if ($xref !== $individual->getXref()) {
122					Individual::getInstance($xref, $individual->getTree())
123						->updateFact($fact_id, $newged, !$keep_chan);
124				}
125			}
126		}
127
128		return $newged;
129	}
130
131	/**
132	 * @param CensusInterface $census
133	 * @param string          $ca_title
134	 * @param string          $ca_place
135	 * @param string          $ca_citation
136	 * @param string[][]      $ca_individuals
137	 * @param string          $ca_notes
138	 *
139	 * @return string
140	 */
141	private function createNoteText(CensusInterface $census, $ca_title, $ca_place, $ca_citation, $ca_individuals, $ca_notes) {
142		$text = $ca_title . "\n" . $ca_citation . "\n" . $ca_place . "\n\n";
143
144		foreach ($census->columns() as $n => $column) {
145			if ($n === 0) {
146				$text .= "\n";
147			} else {
148				$text .= ' | ';
149			}
150			$text .= $column->abbreviation();
151		}
152
153		foreach ($census->columns() as $n => $column) {
154			if ($n === 0) {
155				$text .= "\n";
156			} else {
157				$text .= ' | ';
158			}
159			$text .= '-----';
160		}
161
162		foreach ($ca_individuals as $xref => $columns) {
163			$text .= "\n" . implode(' | ', $columns);
164		}
165
166		return $text . "\n\n" . $ca_notes;
167	}
168
169	/**
170	 * Find a media object.
171	 */
172	private static function mediaFind() {
173		global $WT_TREE;
174
175		$controller = new SimpleController;
176		$filter     = Filter::get('filter');
177		$multiple   = Filter::getBool('multiple');
178
179		$controller
180			->setPageTitle(I18N::translate('Find an individual'))
181			->pageHeader();
182
183		?>
184		<script>
185			function pasterow(id, name, gend, yob, age, bpl) {
186				window.opener.opener.insertRowToTable(id, name, '', gend, '', yob, age, 'Y', '', bpl);
187			}
188
189			function pasteid(id, name, thumb) {
190				if (thumb) {
191					window.opener.paste_id(id, name, thumb);
192					<?php if (!$multiple) {
193					echo 'window.close();';
194				} ?>
195				} else {
196					// GEDFact_assistant ========================
197					if (window.opener.document.getElementById('addlinkQueue')) {
198						window.opener.insertRowToTable(id, name);
199					}
200					window.opener.paste_id(id);
201					if (window.opener.pastename) {
202						window.opener.pastename(name);
203					}
204					<?php if (!$multiple) {
205						echo 'window.close();';
206					} ?>
207				}
208			}
209
210			function checknames(frm) {
211				var button = '';
212				if (document.forms[0].subclick) {
213					button = document.forms[0].subclick.value;
214				}
215				if (frm.filter.value.length < 2 && button !== 'all') {
216					alert('<?= I18N::translate('Please enter more than one character.') ?>');
217					frm.filter.focus();
218					return false;
219				}
220				if (button === 'all') {
221					frm.filter.value = '';
222				}
223				return true;
224			}
225		</script>
226
227		<?php
228		echo '<div>';
229		echo '<table class="list_table width90" border="0">';
230		echo '<tr><td style="padding: 10px;" class="width90">'; // start column for find text header
231		echo $controller->getPageTitle();
232		echo '</td>';
233		echo '</tr>';
234		echo '</table>';
235		echo '<br>';
236		echo '<button onclick="window.close();">', I18N::translate('close'), '</button>';
237		echo '<br>';
238
239		$filter       = trim($filter);
240		$filter_array = explode(' ', preg_replace('/ {2,}/', ' ', $filter));
241		echo '<table class="tabs_table width90"><tr>';
242		$myindilist = FunctionsDb::searchIndividualNames($filter_array, [$WT_TREE]);
243		if ($myindilist) {
244			echo '<td class="list_value_wrap"><ul>';
245			usort($myindilist, '\Fisharebest\Webtrees\GedcomRecord::compare');
246			foreach ($myindilist as $indi) {
247				$nam = e($indi->getFullName());
248				echo "<li><a href=\"#\" onclick=\"pasterow(
249					'" . $indi->getXref() . "' ,
250					'" . $nam . "' ,
251					'" . $indi->getSex() . "' ,
252					'" . $indi->getBirthYear() . "' ,
253					'" . (1901 - $indi->getBirthYear()) . "' ,
254					'" . $indi->getBirthPlace()->getGedcomName() . "'); return false;\">
255					<b>" . $indi->getFullName() . '</b>&nbsp;&nbsp;&nbsp;';
256
257				$born = I18N::translate('Birth');
258				echo '</span><br><span class="list_item">', $born, ' ', $indi->getBirthYear(), '&nbsp;&nbsp;&nbsp;', $indi->getBirthPlace()->getGedcomName(), '</span></a></li>';
259				echo '<hr>';
260			}
261			echo '</ul></td></tr><tr><td class="list_label">', I18N::translate('Total individuals: %s', count($myindilist)), '</tr></td>';
262		} else {
263			echo '<td class="list_value_wrap">';
264			echo I18N::translate('No results found.');
265			echo '</td></tr>';
266		}
267		echo '</table>';
268		echo '</div>';
269	}
270
271	/**
272	 * Search for a media object.
273	 */
274	private static function mediaQuery() {
275		global $WT_TREE;
276
277		$iid2 = Filter::get('iid', WT_REGEX_XREF);
278
279		$controller = new SimpleController;
280		$controller
281			->setPageTitle(I18N::translate('Link to an existing media object'))
282			->pageHeader();
283
284		$record = GedcomRecord::getInstance($iid2, $WT_TREE);
285		if ($record) {
286			$headjs = '';
287			if ($record instanceof Family) {
288				if ($record->getHusband()) {
289					$headjs = $record->getHusband()->getXref();
290				} elseif ($record->getWife()) {
291					$headjs = $record->getWife()->getXref();
292				}
293			}
294			?>
295			<script>
296				function insertId() {
297					if (window.opener.document.getElementById('addlinkQueue')) {
298						// alert('Please move this alert window and examine the contents of the pop-up window, then click OK')
299						window.opener.insertRowToTable('<?= $record->getXref() ?>', '<?= htmlspecialchars($record->getFullName()) ?>', '<?= $headjs ?>');
300						window.close();
301					}
302				}
303			</script>
304			<?php
305		} else {
306			?>
307			<script>
308				function insertId() {
309					window.opener.alert('<?= $iid2 ?> - <?= I18N::translate('Not a valid individual, family, or source ID') ?>');
310					window.close();
311				}
312			</script>
313			<?php
314		}
315		?>
316		<script>window.onLoad = insertId();</script>
317		<?php
318	}
319
320	/**
321	 * Generate an HTML row of data for the census header
322	 * Add prefix cell (store XREF and drag/drop)
323	 * Add suffix cell (delete button)
324	 *
325	 * @param CensusInterface $census
326	 *
327	 * @return string
328	 */
329	public static function censusTableHeader(CensusInterface $census) {
330		$html = '';
331		foreach ($census->columns() as $column) {
332			$html .= '<th class="wt-census-assistant-field" title="' . $column->title() . '">' . $column->abbreviation() . '</th>';
333		}
334
335		return '<tr class="wt-census-assistant-row"><th hidden></th>' . $html . '<th></th></tr>';
336	}
337
338	/**
339	 * Generate an HTML row of data for the census
340	 * Add prefix cell (store XREF and drag/drop)
341	 * Add suffix cell (delete button)
342	 *
343	 * @param CensusInterface $census
344	 *
345	 * @return string
346	 */
347	public static function censusTableEmptyRow(CensusInterface $census) {
348		return '<tr class="wt-census-assistant-row"><td hidden></td>' . str_repeat('<td class="wt-census-assistant-field"><input type="text" class="form-control wt-census-assistant-form-control"></td>', count($census->columns())) . '<td><a class="icon-remove" href="#" title="' . I18N::translate('Remove') . '"></a></td></tr>';
349	}
350
351	/**
352	 * Generate an HTML row of data for the census
353	 * Add prefix cell (store XREF and drag/drop)
354	 * Add suffix cell (delete button)
355	 *
356	 * @param CensusInterface $census
357	 * @param Individual      $individual
358	 * @param Individual      $head
359	 *
360	 * @return string
361	 */
362	public static function censusTableRow(CensusInterface $census, Individual $individual, Individual $head) {
363		$html = '';
364		foreach ($census->columns() as $column) {
365			$html .= '<td class="wt-census-assistant-field"><input class="form-control wt-census-assistant-form-control" type="text" value="' . $column->generate($individual, $head) . '" name="ca_individuals[' . $individual->getXref() . '][]"></td>';
366		}
367
368		return '<tr class="wt-census-assistant-row"><td class="wt-census-assistant-field" hidden>' . $individual->getXref() . '</td>' . $html . '<td class="wt-census-assistant-field"><a class="icon-remove" href="#" title="' . I18N::translate('Remove') . '"></a></td></tr>';
369	}
370}
371