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