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