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\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> '; 256 257 $born = I18N::translate('Birth'); 258 echo '</span><br><span class="list_item">', $born, ' ', $indi->getBirthYear(), ' ', $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