/** * The media firewall passes in an image * this function can manipulate the image however it wants * before returning it back to the media firewall * * @param resource $im * @param Tree $tree * * @return resource */ function applyWatermark($im, Tree $tree) { // text to watermark with $word1_text = $tree->getTitle(); // maximum font size for “word1” ; will be automaticaly reduced to fit in the image $word1_maxsize = 100; // rgb color codes for text $word1_color = '0,0,0'; // ttf font file to use $word1_font = WT_ROOT . Config::FONT_DEJAVU_SANS_TTF; // vertical position for the text to past; possible values are: top, middle or bottom, across $word1_vpos = 'across'; // horizontal position for the text to past in media file; possible values are: left, right, top2bottom, bottom2top // this value is used only if $word1_vpos=across $word1_hpos = 'left'; $word2_text = $_SERVER['HTTP_HOST']; $word2_maxsize = 20; $word2_color = '0,0,0'; $word2_font = WT_ROOT . Config::FONT_DEJAVU_SANS_TTF; $word2_vpos = 'top'; $word2_hpos = 'top2bottom'; embedText($im, $word1_text, $word1_maxsize, $word1_color, $word1_font, $word1_vpos, $word1_hpos); embedText($im, $word2_text, $word2_maxsize, $word2_color, $word2_font, $word2_vpos, $word2_hpos); return $im; }
/** * Send an external email message * Caution! gmail may rewrite the "From" header unless you have added the address to your account. * * @param Tree $tree * @param string $to_email * @param string $to_name * @param string $replyto_email * @param string $replyto_name * @param string $subject * @param string $message * * @return bool */ public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) { try { $mail = Swift_Message::newInstance()->setSubject($subject)->setFrom(Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'))->setTo($to_email, $to_name)->setReplyTo($replyto_email, $replyto_name)->setBody($message, 'text/html')->addPart(Filter::unescapeHtml($message), 'text/plain'); Swift_Mailer::newInstance(self::transport())->send($mail); } catch (Exception $ex) { Log::addErrorLog('Mail: ' . $ex->getMessage()); return false; } return true; }
/** * Send an external email message * Caution! gmail may rewrite the "From" header unless you have added the address to your account. * * @param Tree $tree * @param string $to_email * @param string $to_name * @param string $replyto_email * @param string $replyto_name * @param string $subject * @param string $message * * @return bool */ public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) { try { $mail = new Zend_Mail('UTF-8'); $mail->setSubject($subject)->setBodyHtml($message)->setBodyText(Filter::unescapeHtml($message))->setFrom(Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'))->addTo($to_email, $to_name)->setReplyTo($replyto_email, $replyto_name)->send(self::transport()); } catch (\Exception $ex) { Log::addErrorLog('Mail: ' . $ex->getMessage()); return false; } return true; }
/** * Compute all Sosa ancestors from the user's root individual. * @return bool Result of the computation */ public function computeAll() { $root_id = $this->tree->getUserPreference($this->user, 'MAJ_SOSA_ROOT_ID'); $indi = Individual::getInstance($root_id, $this->tree); if ($indi) { $this->sosa_provider->deleteAll(); $this->addNode($indi, 1); $this->flushTmpSosaTable(true); return true; } return false; }
/** {@inheritDoc} */ public function upgrade() { // add key 'LINK' to FTV_SETTINGS // change options to multidimensional array with array key = tree id. $module_settings = 'FTV_SETTINGS'; $ftv_settings = Database::prepare("SELECT setting_value FROM `##module_setting` WHERE setting_name=?")->execute(array($module_settings))->fetchOne(); $settings = unserialize($ftv_settings); if (!empty($settings)) { foreach ($settings as $setting) { if (!array_key_exists('LINK', $setting)) { $setting['LINK'] = I18N::translate('Descendants of the %s family', $setting['SURNAME']); $new_settings[] = $setting; } } if (isset($new_settings)) { Database::prepare("UPDATE `##module_setting` SET setting_value=? WHERE setting_name=?")->execute(array(serialize($new_settings), $module_settings)); } unset($new_settings); } $module_options = 'FTV_OPTIONS'; $ftv_options = Database::prepare("SELECT setting_value FROM `##module_setting` WHERE setting_name=?")->execute(array($module_options))->fetchOne(); $options = unserialize($ftv_options); if (!empty($options)) { $show_places = array_key_exists('SHOW_PLACES', $options) ? $options['SHOW_PLACES'] : '1'; $country = array_key_exists('COUNTRY', $options) ? $options['COUNTRY'] : ''; $show_occu = array_key_exists('SHOW_OCCU', $options) ? $options['SHOW_OCCU'] : '1'; foreach (Tree::getAll() as $tree) { $new_options[$tree->getTreeId()] = array('SHOW_PLACES' => $show_places, 'COUNTRY' => $country, 'SHOW_OCCU' => $show_occu); } if (isset($new_options)) { Database::prepare("UPDATE `##module_setting` SET setting_value=? WHERE setting_name=?")->execute(array(serialize($new_options), $module_options)); } unset($new_options); } }
/** * Returns an array of the place hierarchy, based on a random example of place within the GEDCOM. * It will look for the longest hierarchy in the tree. * The places are reversed compared to normal GEDCOM structure. * * @return array */ protected function getPlacesHierarchyFromData() { $nb_levels = 0; //Select all '2 PLAC ' tags in the file and create array $places_list = array(); $ged_data = Database::prepare('SELECT i_gedcom AS gedcom' . ' FROM `##individuals`' . ' WHERE i_gedcom LIKE :gedcom AND i_file = :gedcom_id' . ' UNION ALL' . 'SELECT f_gedcom AS gedcom' . ' FROM `##families`' . ' WHERE f_gedcom LIKE :gedcom AND f_file = :gedcom_id')->execute(array('gedcom' => '%\\n2 PLAC %', 'gedcom_id' => $this->tree->getTreeId()))->fetchOneColumn(); foreach ($ged_data as $ged_datum) { $matches = null; preg_match_all('/\\n2 PLAC (.+)/', $ged_datum, $matches); foreach ($matches[1] as $match) { $places_list[$match] = true; } } // Unique list of places $places_list = array_keys($places_list); //sort the array, limit to unique values, and count them usort($places_list, array('I18N', 'strcasecmp')); //calculate maximum no. of levels to display $has_found_good_example = false; foreach ($places_list as $place) { $levels = explode(",", $place); $parts = count($levels); if ($parts >= $nb_levels) { $nb_levels = $parts; if (!$has_found_good_example) { $random_place = $place; if (min(array_map('strlen', $levels)) > 0) { $has_found_good_example = true; } } } } return array_reverse(array_map('trim', explode(',', $random_place))); }
/** * Sometimes, we'll know in advance that we need to load a set of records. * Typically when we load families and their members. * * @param Tree $tree * @param string[] $xrefs */ public static function load(Tree $tree, array $xrefs) { $args = array('tree_id' => $tree->getTreeId()); $placeholders = array(); foreach (array_unique($xrefs) as $n => $xref) { if (!isset(self::$gedcom_record_cache[$tree->getTreeId()][$xref])) { $placeholders[] = ':x' . $n; $args['x' . $n] = $xref; } } if (!empty($placeholders)) { $rows = Database::prepare("SELECT i_id AS xref, i_gedcom AS gedcom" . " FROM `##individuals`" . " WHERE i_file = :tree_id AND i_id IN (" . implode(',', $placeholders) . ")")->execute($args)->fetchAll(); foreach ($rows as $row) { self::getInstance($row->xref, $tree, $row->gedcom); } } }
/** * Sometimes, we'll know in advance that we need to load a set of records. * Typically when we load families and their members. * * @param Tree $tree * @param string[] $xrefs */ public static function load(Tree $tree, array $xrefs) { $sql = ''; $args = array('tree_id' => $tree->getTreeId()); foreach (array_unique($xrefs) as $n => $xref) { if (!isset(self::$gedcom_record_cache[$tree->getTreeId()][$xref])) { $sql .= ($n ? ',:x' : ':x') . $n; $args['x' . $n] = $xref; } } if (count($args) > 1) { $rows = Database::prepare("SELECT i_id AS xref, i_gedcom AS gedcom" . " FROM `##individuals`" . " WHERE i_file = :tree_id AND i_id IN (" . $sql . ")")->execute($args)->fetchAll(); foreach ($rows as $row) { self::getInstance($row->xref, $tree, $row->gedcom); } } }
/** * Return a computed array of statistics about the dispersion of ancestors across the ancestors * at a specified generation. * This statistics cannot be used for generations above 11, as it would cause a out of range in MySQL * * Format: * - key : a base-2 representation of the ancestor at generation G for which exclusive ancestors have been found, * -1 is used for shared ancestors * For instance base2(0100) = base10(4) represent the maternal grand father * - values: number of ancestors exclusively in the ancestors of the ancestor in key * * For instance a result at generation 3 could be : * array ( -1 => 12 -> 12 ancestors are shared by the grand-parents * base10(1) => 32 -> 32 ancestors are exclusive to the paternal grand-father * base10(2) => 25 -> 25 ancestors are exclusive to the paternal grand-mother * base10(4) => 12 -> 12 ancestors are exclusive to the maternal grand-father * base10(8) => 30 -> 30 ancestors are exclusive to the maternal grand-mother * ) * * @param int $gen Reference generation * @return array */ public function getAncestorDispersionForGen($gen) { if (!$this->is_setup || $gen > 11) { return array(); } // Going further than 11 gen will be out of range in the query return Database::prepare('SELECT branches, count(i_id)' . ' FROM (' . ' SELECT i_id,' . ' CASE' . ' WHEN CEIL(LOG2(SUM(branch))) = LOG2(SUM(branch)) THEN SUM(branch)' . ' ELSE -1' . ' END branches' . ' FROM (' . ' SELECT DISTINCT majs_i_id i_id,' . ' POW(2, FLOOR(majs_sosa / POW(2, (majs_gen - :gen))) - POW(2, :gen -1)) branch' . ' FROM `##maj_sosa`' . ' WHERE majs_gedcom_id = :tree_id AND majs_user_id = :user_id' . ' AND majs_gen >= :gen' . ' ) indistat' . ' GROUP BY i_id' . ') grouped' . ' GROUP BY branches')->execute(array('tree_id' => $this->tree->getTreeId(), 'user_id' => $this->user->getUserId(), 'gen' => $gen))->fetchAssoc() ?: array(); }
/** * Get a list of modules which (a) provide a specific function and (b) we have permission to see. * * We cannot currently use auto-loading for modules, as there may be user-defined * modules about which the auto-loader knows nothing. * * @param Tree $tree * @param string $component The type of module, such as "tab", "report" or "menu" * * @return AbstractModule[] */ private static function getActiveModulesByComponent(Tree $tree, $component) { $module_names = Database::prepare("SELECT SQL_CACHE module_name" . " FROM `##module`" . " JOIN `##module_privacy` USING (module_name)" . " WHERE gedcom_id = :tree_id AND component = :component AND status = 'enabled' AND access_level >= :access_level" . " ORDER BY CASE component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name")->execute(array('tree_id' => $tree->getTreeId(), 'component' => $component, 'access_level' => Auth::accessLevel($tree)))->fetchOneColumn(); $array = array(); foreach ($module_names as $module_name) { $interface = '\\Fisharebest\\Webtrees\\Module\\Module' . ucfirst($component) . 'Interface'; $module = self::getModuleByName($module_name); if ($module instanceof $interface) { $array[$module_name] = $module; } } // The order of menus/sidebars/tabs is defined in the database. Others are sorted by name. if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') { uasort($array, function (AbstractModule $x, AbstractModule $y) { return I18N::strcasecmp($x->getTitle(), $y->getTitle()); }); } return $array; }
/** * Upgrade to to the next version */ public function upgrade() { // - changes to the values for the gedcom setting SHOW_RELATIVES_EVENTS $settings = Database::prepare("SELECT gedcom_id, setting_value FROM `##gedcom_setting` WHERE setting_name='SHOW_RELATIVES_EVENTS'")->fetchAssoc(); foreach ($settings as $gedcom_id => $setting) { // Delete old settings $setting = preg_replace('/_(BIRT|MARR|DEAT)_(COUS|MSIB|FSIB|GGCH|NEPH|GGPA)/', '', $setting); $setting = preg_replace('/_FAMC_(RESI_EMIG)/', '', $setting); // Rename settings $setting = preg_replace('/_MARR_(MOTH|FATH|FAMC)/', '_MARR_PARE', $setting); $setting = preg_replace('/_DEAT_(MOTH|FATH)/', '_DEAT_PARE', $setting); // Remove duplicates preg_match_all('/[_A-Z]+/', $setting, $match); // And save Tree::findById($gedcom_id)->setPreference('SHOW_RELATIVES_EVENTS', implode(',', array_unique($match[0]))); } }
/** * Create a header for a (newly-created or already-imported) gedcom file. * * @param Tree $tree * * @return string */ public static function gedcomHeader(Tree $tree) { // Default values for a new header $HEAD = "0 HEAD"; $SOUR = "\n1 SOUR " . WT_WEBTREES . "\n2 NAME " . WT_WEBTREES . "\n2 VERS " . WT_VERSION; $DEST = "\n1 DEST DISKETTE"; $DATE = "\n1 DATE " . strtoupper(date("d M Y")) . "\n2 TIME " . date("H:i:s"); $GEDC = "\n1 GEDC\n2 VERS 5.5.1\n2 FORM Lineage-Linked"; $CHAR = "\n1 CHAR UTF-8"; $FILE = "\n1 FILE " . $tree->getName(); $LANG = ""; $PLAC = "\n1 PLAC\n2 FORM City, County, State/Province, Country"; $COPR = ""; $SUBN = ""; $SUBM = "\n1 SUBM @SUBM@\n0 @SUBM@ SUBM\n1 NAME " . Auth::user()->getUserName(); // The SUBM record is mandatory // Preserve some values from the original header $record = GedcomRecord::getInstance('HEAD', $tree); if ($fact = $record->getFirstFact('PLAC')) { $PLAC = "\n1 PLAC\n2 FORM " . $fact->getAttribute('FORM'); } if ($fact = $record->getFirstFact('LANG')) { $LANG = $fact->getValue(); } if ($fact = $record->getFirstFact('SUBN')) { $SUBN = $fact->getValue(); } if ($fact = $record->getFirstFact('COPR')) { $COPR = $fact->getValue(); } // Link to actual SUBM/SUBN records, if they exist $subn = Database::prepare("SELECT o_id FROM `##other` WHERE o_type=? AND o_file=?")->execute(array('SUBN', $tree->getTreeId()))->fetchOne(); if ($subn) { $SUBN = "\n1 SUBN @{$subn}@"; } $subm = Database::prepare("SELECT o_id FROM `##other` WHERE o_type=? AND o_file=?")->execute(array('SUBM', $tree->getTreeId()))->fetchOne(); if ($subm) { $SUBM = "\n1 SUBM @{$subm}@"; } return $HEAD . $SOUR . $DEST . $DATE . $GEDC . $CHAR . $FILE . $COPR . $LANG . $PLAC . $SUBN . $SUBM . "\n"; }
http_response_code(403); return; } $controller = new AjaxController(); $controller->pageHeader(); // Don't allow the user to cancel the request. We do not want to be left // with an incomplete transaction. ignore_user_abort(true); // Run in a transaction Database::beginTransaction(); // Only allow one process to import each gedcom at a time Database::prepare("SELECT * FROM `##gedcom_chunk` WHERE gedcom_id=? FOR UPDATE")->execute(array($gedcom_id)); // What is the current import status? $row = Database::prepare("SELECT" . " SUM(IF(imported, LENGTH(chunk_data), 0)) AS import_offset," . " SUM(LENGTH(chunk_data)) AS import_total" . " FROM `##gedcom_chunk` WHERE gedcom_id=?")->execute(array($gedcom_id))->fetchOneRow(); if ($row->import_offset == $row->import_total) { Tree::findById($gedcom_id)->setPreference('imported', '1'); // Finished? Show the maintenance links, similar to admin_trees_manage.php Database::commit(); $controller->addInlineJavascript('jQuery("#import' . $gedcom_id . '").addClass("hidden");' . 'jQuery("#actions' . $gedcom_id . '").removeClass("hidden");'); return; } // Calculate progress so far $progress = $row->import_offset / $row->import_total; ?> <div class="progress" id="progress<?php echo $gedcom_id; ?> "> <div class="progress-bar" role="progressbar"
/** * Defined in session.php * * @global Tree $WT_TREE */ global $WT_TREE; use Fisharebest\Webtrees\Controller\PageController; use Fisharebest\Webtrees\Module\CkeditorModule; define('WT_SCRIPT_NAME', 'block_edit.php'); require './includes/session.php'; $block_id = Filter::getInteger('block_id'); $block = Database::prepare("SELECT SQL_CACHE * FROM `##block` WHERE block_id=?")->execute(array($block_id))->fetchOneRow(); // Check access. (1) the block must exist and be enabled, (2) gedcom blocks require // managers, (3) user blocks require the user or an admin $blocks = Module::getActiveBlocks($WT_TREE); if (!$block || !array_key_exists($block->module_name, $blocks) || $block->gedcom_id && !Auth::isManager(Tree::findById($block->gedcom_id)) || $block->user_id && $block->user_id != Auth::id() && !Auth::isAdmin()) { header('Location: ' . WT_BASE_URL); return; } $block = $blocks[$block->module_name]; if (Filter::post('save')) { $ctype = Filter::post('ctype', 'user', 'gedcom'); header('Location: ' . WT_BASE_URL . 'index.php?ctype=' . $ctype . '&ged=' . $WT_TREE->getNameUrl()); $block->configureBlock($block_id); return; } $ctype = FIlter::get('ctype', 'user', 'gedcom'); $controller = new PageController(); $controller->setPageTitle(I18N::translate('Configure') . ' — ' . $block->getTitle())->pageHeader(); if (Module::getModuleByName('ckeditor')) { CkeditorModule::enableEditor($controller);
/** * Places administration. */ private function adminPlaces() { global $WT_TREE; $action = Filter::get('action'); $parent = Filter::get('parent'); $inactive = Filter::getBool('inactive'); $deleteRecord = Filter::get('deleteRecord'); if (!isset($parent)) { $parent = 0; } $controller = new PageController(); $controller->restrictAccess(Auth::isAdmin()); if ($action == 'ExportFile' && Auth::isAdmin()) { $tmp = $this->placeIdToHierarchy($parent); $maxLevel = $this->getHighestLevel(); if ($maxLevel > 8) { $maxLevel = 8; } $tmp[0] = 'places'; $outputFileName = preg_replace('/[:;\\/\\\\(\\)\\{\\}\\[\\] $]/', '_', implode('-', $tmp)) . '.csv'; header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $outputFileName . '"'); echo '"', I18N::translate('Level'), '";"', I18N::translate('Country'), '";'; if ($maxLevel > 0) { echo '"', I18N::translate('State'), '";'; } if ($maxLevel > 1) { echo '"', I18N::translate('County'), '";'; } if ($maxLevel > 2) { echo '"', I18N::translate('City'), '";'; } if ($maxLevel > 3) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 4) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 5) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 6) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 7) { echo '"', I18N::translate('Place'), '";'; } echo '"', I18N::translate('Longitude'), '";"', I18N::translate('Latitude'), '";'; echo '"', I18N::translate('Zoom level'), '";"', I18N::translate('Icon'), '";', WT_EOL; $this->outputLevel($parent); exit; } $controller->setPageTitle(I18N::translate('Google Maps™'))->pageHeader(); ?> <ol class="breadcrumb small"> <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?> </a></li> <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?> </a></li> <li class="active"><?php echo $controller->getPageTitle(); ?> </li> </ol> <ul class="nav nav-tabs nav-justified" role="tablist"> <li role="presentation"> <a href="?mod=googlemap&mod_action=admin_config" role="tab"> <?php echo I18N::translate('Google Maps™ preferences'); ?> </a> </li> <li role="presentation" class="active"> <a href="#"> <?php echo I18N::translate('Geographic data'); ?> </a> </li> <li role="presentation"> <a href="?mod=googlemap&mod_action=admin_placecheck"> <?php echo I18N::translate('Place check'); ?> </a> </li> </ul> <h2><?php echo I18N::translate('Geographic data'); ?> </h2> <?php if ($action == 'ImportGedcom') { $placelist = array(); $j = 0; $gedcom_records = Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_file=? UNION ALL SELECT f_gedcom FROM `##families` WHERE f_file=?")->execute(array($WT_TREE->getTreeId(), $WT_TREE->getTreeId()))->fetchOneColumn(); foreach ($gedcom_records as $gedrec) { $i = 1; $placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i); while (!empty($placerec)) { if (preg_match("/2 PLAC (.+)/", $placerec, $match)) { $placelist[$j] = array(); $placelist[$j]['place'] = trim($match[1]); if (preg_match("/4 LATI (.*)/", $placerec, $match)) { $placelist[$j]['lati'] = trim($match[1]); if ($placelist[$j]['lati'][0] != 'N' && $placelist[$j]['lati'][0] != 'S') { if ($placelist[$j]['lati'] < 0) { $placelist[$j]['lati'][0] = 'S'; } else { $placelist[$j]['lati'] = 'N' . $placelist[$j]['lati']; } } } else { $placelist[$j]['lati'] = null; } if (preg_match("/4 LONG (.*)/", $placerec, $match)) { $placelist[$j]['long'] = trim($match[1]); if ($placelist[$j]['long'][0] != 'E' && $placelist[$j]['long'][0] != 'W') { if ($placelist[$j]['long'] < 0) { $placelist[$j]['long'][0] = 'W'; } else { $placelist[$j]['long'] = 'E' . $placelist[$j]['long']; } } } else { $placelist[$j]['long'] = null; } $j = $j + 1; } $i = $i + 1; $placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i); } } asort($placelist); $prevPlace = ''; $prevLati = ''; $prevLong = ''; $placelistUniq = array(); $j = 0; foreach ($placelist as $k => $place) { if ($place['place'] != $prevPlace) { $placelistUniq[$j] = array(); $placelistUniq[$j]['place'] = $place['place']; $placelistUniq[$j]['lati'] = $place['lati']; $placelistUniq[$j]['long'] = $place['long']; $j = $j + 1; } elseif ($place['place'] == $prevPlace && ($place['lati'] != $prevLati || $place['long'] != $prevLong)) { if ($placelistUniq[$j - 1]['lati'] == 0 || $placelistUniq[$j - 1]['long'] == 0) { $placelistUniq[$j - 1]['lati'] = $place['lati']; $placelistUniq[$j - 1]['long'] = $place['long']; } elseif ($place['lati'] != '0' || $place['long'] != '0') { echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '<br>'; } } $prevPlace = $place['place']; $prevLati = $place['lati']; $prevLong = $place['long']; } $highestIndex = $this->getHighestIndex(); $default_zoom_level = array(4, 7, 10, 12); foreach ($placelistUniq as $k => $place) { $parent = preg_split('/ *, */', $place['place']); $parent = array_reverse($parent); $parent_id = 0; for ($i = 0; $i < count($parent); $i++) { if (!isset($default_zoom_level[$i])) { $default_zoom_level[$i] = $default_zoom_level[$i - 1]; } $escparent = $parent[$i]; if ($escparent == '') { $escparent = 'Unknown'; } $row = Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ?")->execute(array($i, $parent_id, $escparent))->fetchOneRow(); if ($i < count($parent) - 1) { // Create higher-level places, if necessary if (empty($row)) { $highestIndex++; Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom) VALUES (?, ?, ?, ?, ?)")->execute(array($highestIndex, $parent_id, $i, $escparent, $default_zoom_level[$i])); echo Filter::escapeHtml($escparent), '<br>'; $parent_id = $highestIndex; } else { $parent_id = $row->pl_id; } } else { // Create lowest-level place, if necessary if (empty($row->pl_id)) { $highestIndex++; Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom) VALUES (?, ?, ?, ?, ?, ?, ?)")->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $default_zoom_level[$i])); echo Filter::escapeHtml($escparent), '<br>'; } else { if (empty($row->pl_long) && empty($row->pl_lati) && $place['lati'] != '0' && $place['long'] != '0') { Database::prepare("UPDATE `##placelocation` SET pl_lati=?, pl_long=? WHERE pl_id=?")->execute(array($place['lati'], $place['long'], $row->pl_id)); echo Filter::escapeHtml($escparent), '<br>'; } } } } } $parent = 0; } if ($action === 'ImportFile') { $placefiles = $this->findFiles(WT_MODULES_DIR . 'googlemap/extra'); sort($placefiles); ?> <form class="form-horizontal" method="post" enctype="multipart/form-data" id="importfile" name="importfile" action="module.php?mod=googlemap&mod_action=admin_places&action=ImportFile2"> <!-- PLACES FILE --> <div class="form-group"> <label class="control-label col-sm-4" for="placesfile"> <?php echo I18N::translate('File containing places (CSV)'); ?> </label> <div class="col-sm-8"> <div class="btn btn-default"> <input id="placesfile" type="file" name="placesfile"> </div> </div> </div> <!-- LOCAL FILE --> <?php if (count($placefiles) > 0) { ?> <div class="form-group"> <label class="control-label col-sm-4" for="localfile"> <?php echo I18N::translate('Server file containing places (CSV)'); ?> </label> <div class="col-sm-8"> <div class="input-group"> <span class="input-group-addon"> <?php echo WT_MODULES_DIR . 'googlemap/extra/'; ?> </span> <?php foreach ($placefiles as $p => $placefile) { unset($placefiles[$p]); $p = Filter::escapeHtml($placefile); if (substr($placefile, 0, 1) == "/") { $placefiles[$p] = substr($placefile, 1); } else { $placefiles[$p] = $placefile; } } echo FunctionsEdit::selectEditControl('localfile', $placefiles, '', '', 'class="form-control"'); ?> </div> </div> </div> <?php } ?> <!-- CLEAR DATABASE --> <fieldset class="form-group"> <legend class="control-label col-sm-4"> <?php echo I18N::translate('Delete all existing geographic data before importing the file.'); ?> </legend> <div class="col-sm-8"> <?php echo FunctionsEdit::editFieldYesNo('cleardatabase', 0, 'class="radio-inline"'); ?> </div> </fieldset> <!-- UPDATE ONLY --> <fieldset class="form-group"> <legend class="control-label col-sm-4"> <?php echo I18N::translate('Do not create new locations, just import coordinates for existing locations.'); ?> </legend> <div class="col-sm-8"> <?php echo FunctionsEdit::editFieldYesNo('updateonly', 0, 'class="radio-inline"'); ?> </div> </fieldset> <!-- OVERWRITE DATA --> <fieldset class="form-group"> <legend class="control-label col-sm-4"> <?php echo I18N::translate('Overwrite existing coordinates.'); ?> </legend> <div class="col-sm-8"> <?php echo FunctionsEdit::editFieldYesNo('overwritedata', 0, 'class="radio-inline"'); ?> </div> </fieldset> <!-- SAVE BUTTON --> <div class="form-group"> <div class="col-sm-offset-4 col-sm-8"> <button type="submit" class="btn btn-primary"> <i class="fa fa-check"></i> <?php echo I18N::translate('Continue adding'); ?> </button> </div> </div> </form> <?php exit; } if ($action === 'ImportFile2') { $country_names = array(); $stats = new Stats($WT_TREE); foreach ($stats->iso3166() as $key => $value) { $country_names[$key] = I18N::translate($key); } if (Filter::postBool('cleardatabase')) { Database::exec("DELETE FROM `##placelocation` WHERE 1=1"); } if (!empty($_FILES['placesfile']['tmp_name'])) { $lines = file($_FILES['placesfile']['tmp_name']); } elseif (!empty($_REQUEST['localfile'])) { $lines = file(WT_MODULES_DIR . 'googlemap/extra' . $_REQUEST['localfile']); } // Strip BYTE-ORDER-MARK, if present if (!empty($lines[0]) && substr($lines[0], 0, 3) === WT_UTF8_BOM) { $lines[0] = substr($lines[0], 3); } asort($lines); $highestIndex = $this->getHighestIndex(); $placelist = array(); $j = 0; $maxLevel = 0; foreach ($lines as $p => $placerec) { $fieldrec = explode(';', $placerec); if ($fieldrec[0] > $maxLevel) { $maxLevel = $fieldrec[0]; } } $fields = count($fieldrec); $set_icon = true; if (!is_dir(WT_MODULES_DIR . 'googlemap/places/flags/')) { $set_icon = false; } foreach ($lines as $p => $placerec) { $fieldrec = explode(';', $placerec); if (is_numeric($fieldrec[0]) && $fieldrec[0] <= $maxLevel) { $placelist[$j] = array(); $placelist[$j]['place'] = ''; for ($ii = $fields - 4; $ii > 1; $ii--) { if ($fieldrec[0] > $ii - 2) { $placelist[$j]['place'] .= $fieldrec[$ii] . ','; } } foreach ($country_names as $countrycode => $countryname) { if ($countrycode == strtoupper($fieldrec[1])) { $fieldrec[1] = $countryname; break; } } $placelist[$j]['place'] .= $fieldrec[1]; $placelist[$j]['long'] = $fieldrec[$fields - 4]; $placelist[$j]['lati'] = $fieldrec[$fields - 3]; $placelist[$j]['zoom'] = $fieldrec[$fields - 2]; if ($set_icon) { $placelist[$j]['icon'] = trim($fieldrec[$fields - 1]); } else { $placelist[$j]['icon'] = ''; } $j = $j + 1; } } $prevPlace = ''; $prevLati = ''; $prevLong = ''; $placelistUniq = array(); $j = 0; foreach ($placelist as $k => $place) { if ($place['place'] != $prevPlace) { $placelistUniq[$j] = array(); $placelistUniq[$j]['place'] = $place['place']; $placelistUniq[$j]['lati'] = $place['lati']; $placelistUniq[$j]['long'] = $place['long']; $placelistUniq[$j]['zoom'] = $place['zoom']; $placelistUniq[$j]['icon'] = $place['icon']; $j = $j + 1; } elseif ($place['place'] == $prevPlace && ($place['lati'] != $prevLati || $place['long'] != $prevLong)) { if ($placelistUniq[$j - 1]['lati'] == 0 || $placelistUniq[$j - 1]['long'] == 0) { $placelistUniq[$j - 1]['lati'] = $place['lati']; $placelistUniq[$j - 1]['long'] = $place['long']; $placelistUniq[$j - 1]['zoom'] = $place['zoom']; $placelistUniq[$j - 1]['icon'] = $place['icon']; } elseif ($place['lati'] != '0' || $place['long'] != '0') { echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '<br>'; } } $prevPlace = $place['place']; $prevLati = $place['lati']; $prevLong = $place['long']; } $default_zoom_level = array(); $default_zoom_level[0] = 4; $default_zoom_level[1] = 7; $default_zoom_level[2] = 10; $default_zoom_level[3] = 12; foreach ($placelistUniq as $k => $place) { $parent = explode(',', $place['place']); $parent = array_reverse($parent); $parent_id = 0; for ($i = 0; $i < count($parent); $i++) { $escparent = $parent[$i]; if ($escparent == '') { $escparent = 'Unknown'; } $row = Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom, pl_icon FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place")->execute(array($i, $parent_id, $escparent))->fetchOneRow(); if (empty($row)) { // this name does not yet exist: create entry if (!Filter::postBool('updateonly')) { $highestIndex = $highestIndex + 1; if ($i + 1 == count($parent)) { $zoomlevel = $place['zoom']; } elseif (isset($default_zoom_level[$i])) { $zoomlevel = $default_zoom_level[$i]; } else { $zoomlevel = $this->getSetting('GM_MAX_ZOOM'); } if ($place['lati'] == '0' || $place['long'] == '0' || $i + 1 < count($parent)) { Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?)")->execute(array($highestIndex, $parent_id, $i, $escparent, $zoomlevel, $place['icon'])); } else { //delete leading zero $pl_lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $place['lati']); $pl_long = str_replace(array('E', 'W', ','), array('', '-', '.'), $place['long']); if ($pl_lati >= 0) { $place['lati'] = 'N' . abs($pl_lati); } elseif ($pl_lati < 0) { $place['lati'] = 'S' . abs($pl_lati); } if ($pl_long >= 0) { $place['long'] = 'E' . abs($pl_long); } elseif ($pl_long < 0) { $place['long'] = 'W' . abs($pl_long); } Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $zoomlevel, $place['icon'])); } $parent_id = $highestIndex; } } else { $parent_id = $row->pl_id; if (Filter::postBool('overwritedata') && $i + 1 == count($parent)) { Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ?, pl_zoom = ?, pl_icon = ? WHERE pl_id = ?")->execute(array($place['lati'], $place['long'], $place['zoom'], $place['icon'], $parent_id)); } else { // Update only if existing data is missing if (!$row->pl_long && !$row->pl_lati) { Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ? WHERE pl_id = ?")->execute(array($place['lati'], $place['long'], $parent_id)); } if (!$row->pl_icon && $place['icon']) { Database::prepare("UPDATE `##placelocation` SET pl_icon = ? WHERE pl_id = ?")->execute(array($place['icon'], $parent_id)); } } } } } $parent = 0; } if ($action == 'DeleteRecord') { $exists = Database::prepare("SELECT 1 FROM `##placelocation` WHERE pl_parent_id=?")->execute(array($deleteRecord))->fetchOne(); if (!$exists) { Database::prepare("DELETE FROM `##placelocation` WHERE pl_id=?")->execute(array($deleteRecord)); } else { echo '<table class="facts_table"><tr><td>', I18N::translate('Location not removed: this location contains sub-locations'), '</td></tr></table>'; } } ?> <script> function updateList(inactive) { window.location.href='<?php if (strstr($_SERVER['REQUEST_URI'], '&inactive', true)) { $uri = strstr($_SERVER['REQUEST_URI'], '&inactive', true); } else { $uri = $_SERVER['REQUEST_URI']; } echo $uri, '&inactive='; ?> '+inactive; } function edit_place_location(placeid) { window.open('module.php?mod=googlemap&mod_action=places_edit&action=update&placeid='+placeid, '_blank', gmap_window_specs); return false; } function add_place_location(placeid) { window.open('module.php?mod=googlemap&mod_action=places_edit&action=add&placeid='+placeid, '_blank', gmap_window_specs); return false; } function delete_place(placeid) { var answer=confirm('<?php echo I18N::translate('Remove this location?'); ?> '); if (answer == true) { window.location = '<?php echo Functions::getQueryUrl(array('action' => 'DeleteRecord')); ?> &action=DeleteRecord&deleteRecord=' + placeid; } } </script> <p id="gm_breadcrumb"> <?php $where_am_i = $this->placeIdToHierarchy($parent); foreach (array_reverse($where_am_i, true) as $id => $place) { if ($id == $parent) { if ($place != 'Unknown') { echo Filter::escapeHtml($place); } else { echo I18N::translate('unknown'); } } else { echo '<a href="module.php?mod=googlemap&mod_action=admin_places&parent=', $id, '&inactive=', $inactive, '">'; if ($place != 'Unknown') { echo Filter::escapeHtml($place), '</a>'; } else { echo I18N::translate('unknown'), '</a>'; } } echo ' - '; } ?> <a href="module.php?mod=googlemap&mod_action=admin_places&parent=0&inactive=', $inactive, '"><?php echo I18N::translate('Top level'); ?> </a> </p> <form class="form-inline" name="active" method="post" action="module.php?mod=googlemap&mod_action=admin_places&parent=', $parent, '&inactive=', $inactive, '"> <div class="checkbox"> <label for="inactive"> <?php echo FunctionsEdit::checkbox('inactive', $inactive, 'onclick="updateList(this.checked)"'); ?> <?php echo I18N::translate('Show inactive places'); ?> </label> </div> <p class="small text-muted"> <?php echo I18N::translate('By default, the list shows only those places which can be found in your family trees. You may have details for other places, such as those imported in bulk from an external file. Selecting this option will show all places, including ones that are not currently used.'); ?> <?php echo I18N::translate('If you have a large number of inactive places, it can be slow to generate the list.'); ?> </p> </form> <?php $placelist = $this->getPlaceListLocation($parent, $inactive); echo '<div class="gm_plac_edit">'; echo '<table class="table table-bordered table-condensed table-hover"><tr>'; echo '<th>', GedcomTag::getLabel('PLAC'), '</th>'; echo '<th>', GedcomTag::getLabel('LATI'), '</th>'; echo '<th>', GedcomTag::getLabel('LONG'), '</th>'; echo '<th>', I18N::translate('Zoom level'), '</th>'; echo '<th>', I18N::translate('Icon'), '</th>'; echo '<th>'; echo I18N::translate('Edit'), '</th><th>', I18N::translate('Delete'), '</th></tr>'; if (count($placelist) == 0) { echo '<tr><td colspan="7">', I18N::translate('No places found'), '</td></tr>'; } foreach ($placelist as $place) { echo '<tr><td><a href="module.php?mod=googlemap&mod_action=admin_places&parent=', $place['place_id'], '&inactive=', $inactive, '">'; if ($place['place'] != 'Unknown') { echo Filter::escapeHtml($place['place']), '</a></td>'; } else { echo I18N::translate('unknown'), '</a></td>'; } echo '<td>', $place['lati'], '</td>'; echo '<td>', $place['long'], '</td>'; echo '<td>', $place['zoom'], '</td>'; echo '<td>'; if ($place['icon']) { echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place['icon'], '" width="25" height="15">'; } else { if ($place['lati'] || $place['long']) { echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/mm_20_red.png">'; } else { echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/mm_20_yellow.png">'; } } echo '</td>'; echo '<td class="narrow"><a href="#" onclick="edit_place_location(', $place['place_id'], ');return false;" class="icon-edit" title="', I18N::translate('Edit'), '"></a></td>'; $noRows = Database::prepare("SELECT COUNT(pl_id) FROM `##placelocation` WHERE pl_parent_id=?")->execute(array($place['place_id']))->fetchOne(); if ($noRows == 0) { ?> <td><a href="#" onclick="delete_place(<?php echo $place['place_id']; ?> );return false;" class="icon-delete" title="<?php echo I18N::translate('Remove'); ?> "></a></td> <?php } else { ?> <td><i class="icon-delete-grey"></i></td> <?php } ?> </tr> <?php } ?> </table> </div> <hr> <form class="form-horizontal" action="?" onsubmit="add_place_location(this.parent_id.options[this.parent_id.selectedIndex].value); return false;"> <div class="form-group"> <label class="form-control-static col-sm-4" for="parent_id"> <?php echo I18N::translate('Add a new geographic location'); ?> </label> <div class="col-sm-8"> <div class="col-sm-6"> <?php echo FunctionsEdit::selectEditControl('parent_id', $where_am_i, I18N::translate('Top level'), $parent, 'class="form-control"'); ?> </div> <button type="submit" class="btn btn-default"> <i class="fa fa-plus"></i> <?php echo I18N::translate('Add'); ?> </button> </div> </div> </form> <form class="form-horizontal" action="module.php" method="get"> <input type="hidden" name="mod" value="googlemap"> <input type="hidden" name="mod_action" value="admin_places"> <input type="hidden" name="action" value="ImportGedcom"> <div class="form-group"> <label class="form-control-static col-sm-4" for="ged"> <?php echo I18N::translate('Import all places from a family tree'); ?> </label> <div class="col-sm-8"> <div class="col-sm-6"> <?php echo FunctionsEdit::selectEditControl('ged', Tree::getNameList(), null, $WT_TREE->getName(), 'class="form-control"'); ?> </div> <button type="submit" class="btn btn-default"> <i class="fa fa-upload"></i> <?php echo I18N::translate('Import'); ?> </button> </div> </div> </form> <form class="form-horizontal" action="module.php" method="get"> <input type="hidden" name="mod" value="googlemap"> <input type="hidden" name="mod_action" value="admin_places"> <input type="hidden" name="action" value="ImportFile"> <div class="form-group"> <label class="form-control-static col-sm-4"> <?php echo I18N::translate('Upload geographic data'); ?> </label> <div class="col-sm-8"> <div class="col-sm-6"> <button type="submit" class="btn btn-default"> <i class="fa fa-upload"></i> <?php echo I18N::translate('Upload'); ?> </button> </div> </div> </div> </form> <form class="form-horizontal" action="module.php" method="get"> <input type="hidden" name="mod" value="googlemap"> <input type="hidden" name="mod_action" value="admin_places"> <input type="hidden" name="action" value="ExportFile"> <div class="form-group"> <label class="form-control-static col-sm-4"> <?php echo I18N::translate('Download geographic data'); ?> </label> <div class="col-sm-8"> <div class="col-sm-6"> <?php echo FunctionsEdit::selectEditControl('parent', $where_am_i, I18N::translate('All'), $WT_TREE->getTreeId(), 'class="form-control"'); ?> </div> <button type="submit" class="btn btn-default"> <i class="fa fa-download"></i> <?php echo I18N::translate('Download'); ?> </button> </div> </div> </form> <?php }
echo I18N::translate('User'); ?> </label> <?php echo FunctionsEdit::selectEditControl('user', $users_array, '', $user, 'class="form-control"'); ?> </div> <div class="form-group col-xs-6 col-md-3"> <label for="gedc"> <?php echo I18N::translate('Family tree'); ?> </label> <?php echo FunctionsEdit::selectEditControl('gedc', Tree::getNameList(), '', $gedc, Auth::isAdmin() ? 'class="form-control"' : 'disabled class="form-control"'); ?> </div> </div> <div class="row text-center"> <button type="submit" class="btn btn-primary"> <?php echo I18N::translate('Filter'); ?> </button> <button type="submit" class="btn btn-primary" onclick="document.logs.action.value='export';return true;" <?php echo $action === 'show' ? '' : 'disabled'; ?> >
/** * Search for individuals in a tree. * * @param Tree $tree Search this tree * @param string $query Search for this text * * @return string */ private function search(Tree $tree, $query) { if (strlen($query) < 2) { return ''; } $rows = Database::prepare("SELECT i_id AS xref, i_gedcom AS gedcom" . " FROM `##individuals`, `##name`" . " WHERE (i_id LIKE CONCAT('%', :query_1, '%') OR n_sort LIKE CONCAT('%', :query_2, '%'))" . " AND i_id = n_id AND i_file = n_file AND i_file = :tree_id" . " ORDER BY n_sort COLLATE :collation" . " LIMIT 50")->execute(array('query_1' => $query, 'query_2' => $query, 'tree_id' => $tree->getTreeId(), 'collation' => I18N::collation()))->fetchAll(); $out = '<ul>'; foreach ($rows as $row) { $person = Individual::getInstance($row->xref, $tree, $row->gedcom); if ($person->canShowName()) { $out .= '<li><a href="' . $person->getHtmlUrl() . '">' . $person->getSexImage() . ' ' . $person->getFullName() . ' '; if ($person->canShow()) { $bd = $person->getLifeSpan(); if (!empty($bd)) { $out .= ' (' . $bd . ')'; } } $out .= '</a></li>'; } } $out .= '</ul>'; return $out; }
<?php echo I18N::translate('After creating the family tree, you will be able to import data from a GEDCOM file.'); ?> </p> </div> </div> </form> </div> </div> </div> <?php } ?> <!-- display link to PhpGedView-WT transfer wizard on first visit to this page, before any GEDCOM is loaded --> <?php if (count(Tree::GetAll()) === 0 && count(User::all()) === 1) { ?> <div class="panel panel-default"> <div class="panel-heading"> <h2 class="panel-title"> <i class="fa fa-fw fa-magic"></i> <a data-toggle="collapse" data-parent="#accordion" href="#pgv-import-wizard"> <?php echo I18N::translate('PhpGedView to webtrees transfer wizard'); ?> </a> </h2> </div> <div id="pgv-import-wizard" class="panel-collapse collapse"> <div class="panel-body"> <p>
/** * Performs a search and replace * * @param Tree $tree */ private function searchAndReplace(Tree $tree) { $this->generalSearch(); //-- don't try to make any changes if nothing was found if (!$this->myindilist && !$this->myfamlist && !$this->mysourcelist && !$this->mynotelist) { return; } Log::addEditLog("Search And Replace old:" . $this->query . " new:" . $this->replace); $adv_name_tags = preg_split("/[\\s,;: ]+/", $tree->getPreference('ADVANCED_NAME_FACTS')); $name_tags = array_unique(array_merge(Config::standardNameFacts(), $adv_name_tags)); $name_tags[] = '_MARNM'; $records_updated = 0; foreach ($this->myindilist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } else { if ($this->replaceNames) { foreach ($name_tags as $tag) { $new_record = preg_replace("~(\\d) " . $tag . " (.*)" . $this->query . "(.*)~i", "\$1 " . $tag . " \$2" . $this->replace . "\$3", $new_record); } } if ($this->replacePlaces) { if ($this->replacePlacesWord) { $new_record = preg_replace('~(\\d) PLAC (.*)([,\\W\\s])' . $this->query . '([,\\W\\s])~i', "\$1 PLAC \$2\$3" . $this->replace . "\$4", $new_record); } else { $new_record = preg_replace("~(\\d) PLAC (.*)" . $this->query . "(.*)~i", "\$1 PLAC \$2" . $this->replace . "\$3", $new_record); } } } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record !== $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->myindilist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s individual has been updated.', '%s individuals have been updated.', $records_updated, I18N::number($records_updated))); } $records_updated = 0; foreach ($this->myfamlist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } else { if ($this->replacePlaces) { if ($this->replacePlacesWord) { $new_record = preg_replace('~(\\d) PLAC (.*)([,\\W\\s])' . $this->query . '([,\\W\\s])~i', "\$1 PLAC \$2\$3" . $this->replace . "\$4", $new_record); } else { $new_record = preg_replace("~(\\d) PLAC (.*)" . $this->query . "(.*)~i", "\$1 PLAC \$2" . $this->replace . "\$3", $new_record); } } } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record !== $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->myfamlist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s family has been updated.', '%s families have been updated.', $records_updated, I18N::number($records_updated))); } $records_updated = 0; foreach ($this->mysourcelist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } else { if ($this->replaceNames) { $new_record = preg_replace("~(\\d) TITL (.*)" . $this->query . "(.*)~i", "\$1 TITL \$2" . $this->replace . "\$3", $new_record); $new_record = preg_replace("~(\\d) ABBR (.*)" . $this->query . "(.*)~i", "\$1 ABBR \$2" . $this->replace . "\$3", $new_record); } if ($this->replacePlaces) { if ($this->replacePlacesWord) { $new_record = preg_replace('~(\\d) PLAC (.*)([,\\W\\s])' . $this->query . '([,\\W\\s])~i', "\$1 PLAC \$2\$3" . $this->replace . "\$4", $new_record); } else { $new_record = preg_replace("~(\\d) PLAC (.*)" . $this->query . "(.*)~i", "\$1 PLAC \$2" . $this->replace . "\$3", $new_record); } } } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record !== $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->mysourcelist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s source has been updated.', '%s sources have been updated.', $records_updated, I18N::number($records_updated))); } $records_updated = 0; foreach ($this->mynotelist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record != $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->mynotelist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s note has been updated.', '%s notes have been updated.', $records_updated, I18N::number($records_updated))); } }
/** * Provide a form to manage the FAQs. */ private function config() { global $WT_TREE; $controller = new PageController(); $controller->restrictAccess(Auth::isAdmin())->setPageTitle(I18N::translate('Frequently asked questions'))->pageHeader(); $faqs = Database::prepare("SELECT block_id, block_order, gedcom_id, bs1.setting_value AS header, bs2.setting_value AS faqbody" . " FROM `##block` b" . " JOIN `##block_setting` bs1 USING (block_id)" . " JOIN `##block_setting` bs2 USING (block_id)" . " WHERE module_name = :module_name" . " AND bs1.setting_name = 'header'" . " AND bs2.setting_name = 'faqbody'" . " AND IFNULL(gedcom_id, :tree_id_1) = :tree_id_2" . " ORDER BY block_order")->execute(array('module_name' => $this->getName(), 'tree_id_1' => $WT_TREE->getTreeId(), 'tree_id_2' => $WT_TREE->getTreeId()))->fetchAll(); $min_block_order = Database::prepare("SELECT MIN(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchOne(); $max_block_order = Database::prepare("SELECT MAX(block_order) FROM `##block` WHERE module_name = 'faq' AND (gedcom_id = :tree_id OR gedcom_id IS NULL)")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchOne(); ?> <ol class="breadcrumb small"> <li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?> </a></li> <li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?> </a></li> <li class="active"><?php echo $controller->getPageTitle(); ?> </li> </ol> <h2><?php echo $controller->getPageTitle(); ?> </h2> <p> <?php echo I18N::translate('FAQs are lists of questions and answers, which allow you to explain the site’s rules, policies, and procedures to your visitors. Questions are typically concerned with privacy, copyright, user-accounts, unsuitable content, requirement for source-citations, etc.'); ?> <?php echo I18N::translate('You may use HTML to format the answer and to add links to other websites.'); ?> </p> <form class="form form-inline"> <label for="ged" class="sr-only"> <?php echo I18N::translate('Family tree'); ?> </label> <input type="hidden" name="mod" value="<?php echo $this->getName(); ?> "> <input type="hidden" name="mod_action" value="admin_config"> <?php echo FunctionsEdit::selectEditControl('ged', Tree::getNameList(), null, $WT_TREE->getName(), 'class="form-control"'); ?> <input type="submit" class="btn btn-primary" value="<?php echo I18N::translate('show'); ?> "> </form> <p> <a href="module.php?mod=<?php echo $this->getName(); ?> &mod_action=admin_edit" class="btn btn-default"> <i class="fa fa-plus"></i> <?php echo I18N::translate('Add an FAQ item'); ?> </a> </p> <?php echo '<table class="table table-bordered">'; if (empty($faqs)) { echo '<tr><td class="error center" colspan="5">', I18N::translate('The FAQ list is empty.'), '</td></tr></table>'; } else { foreach ($faqs as $faq) { // NOTE: Print the position of the current item echo '<tr class="faq_edit_pos"><td>'; echo I18N::translate('#%s', $faq->block_order + 1), ' '; if ($faq->gedcom_id === null) { echo I18N::translate('All'); } else { echo $WT_TREE->getTitleHtml(); } echo '</td>'; // NOTE: Print the edit options of the current item echo '<td>'; if ($faq->block_order == $min_block_order) { echo ' '; } else { echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_moveup&block_id=', $faq->block_id, '"><i class="fa fa-arrow-up"></i></i> ', I18N::translate('Move up'), '</a>'; } echo '</td><td>'; if ($faq->block_order == $max_block_order) { echo ' '; } else { echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_movedown&block_id=', $faq->block_id, '"><i class="fa fa-arrow-down"></i></i> ', I18N::translate('Move down'), '</a>'; } echo '</td><td>'; echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_edit&block_id=', $faq->block_id, '"><i class="fa fa-pencil"></i> ', I18N::translate('Edit'), '</a>'; echo '</td><td>'; echo '<a href="module.php?mod=', $this->getName(), '&mod_action=admin_delete&block_id=', $faq->block_id, '" onclick="return confirm(\'', I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeHtml($faq->header)), '\');"><i class="fa fa-trash"></i> ', I18N::translate('Delete'), '</a>'; echo '</td></tr>'; // NOTE: Print the title text of the current item echo '<tr><td colspan="5">'; echo '<div class="faq_edit_item">'; echo '<div class="faq_edit_title">', $faq->header, '</div>'; // NOTE: Print the body text of the current item echo '<div class="faq_edit_content">', substr($faq->faqbody, 0, 1) == '<' ? $faq->faqbody : nl2br($faq->faqbody, false), '</div></div></td></tr>'; } echo '</table>'; } }
/** * Find records that have changed since a given julian day * * @param Tree $tree Changes for which tree * @param int $jd Julian day * * @return GedcomRecord[] List of records with changes */ private function getRecentChanges(Tree $tree, $jd) { $sql = "SELECT d_gid FROM `##dates`" . " WHERE d_fact='CHAN' AND d_julianday1 >= :jd AND d_file = :tree_id"; $vars = array('jd' => $jd, 'tree_id' => $tree->getTreeId()); $xrefs = Database::prepare($sql)->execute($vars)->fetchOneColumn(); $records = array(); foreach ($xrefs as $xref) { $record = GedcomRecord::getInstance($xref, $tree); if ($record->canShow()) { $records[] = $record; } } return $records; }
/** * Autocomplete search for families. * * @param Tree $tree Search this tree * @param string $query Search for this text * * @return string */ private function search(Tree $tree, $query) { if (strlen($query) < 2) { return ''; } $rows = Database::prepare("SELECT i_id AS xref" . " FROM `##individuals`, `##name`" . " WHERE (i_id LIKE CONCAT('%', :query_1, '%') OR n_sort LIKE CONCAT('%', :query_2, '%'))" . " AND i_id = n_id AND i_file = n_file AND i_file = :tree_id" . " ORDER BY n_sort COLLATE :collation" . " LIMIT 50")->execute(array('query_1' => $query, 'query_2' => $query, 'tree_id' => $tree->getTreeId(), 'collation' => I18N::collation()))->fetchAll(); $ids = array(); foreach ($rows as $row) { $ids[] = $row->xref; } $vars = array(); if (empty($ids)) { //-- no match : search for FAM id $where = "f_id LIKE CONCAT('%', ?, '%')"; $vars[] = $query; } else { //-- search for spouses $qs = implode(',', array_fill(0, count($ids), '?')); $where = "(f_husb IN ({$qs}) OR f_wife IN ({$qs}))"; $vars = array_merge($vars, $ids, $ids); } $vars[] = $tree->getTreeId(); $rows = Database::prepare("SELECT f_id AS xref, f_file AS gedcom_id, f_gedcom AS gedcom FROM `##families` WHERE {$where} AND f_file=?")->execute($vars)->fetchAll(); $out = '<ul>'; foreach ($rows as $row) { $family = Family::getInstance($row->xref, $tree, $row->gedcom); if ($family->canShowName()) { $out .= '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . ' '; if ($family->canShow()) { $marriage_year = $family->getMarriageYear(); if ($marriage_year) { $out .= ' (' . $marriage_year . ')'; } } $out .= '</a></li>'; } } $out .= '</ul>'; return $out; }
// add/update FunctionsImport::updateRecord($change->new_gedcom, $change->gedcom_id, false); } Database::prepare("UPDATE `##change` SET status='accepted' WHERE change_id=?")->execute(array($change->change_id)); Log::addEditLog("Accepted change {$change->change_id} for {$change->xref} / {$change->gedcom_name} into database"); } break; } $changed_gedcoms = Database::prepare("SELECT g.gedcom_name" . " FROM `##change` c" . " JOIN `##gedcom` g USING (gedcom_id)" . " WHERE c.status='pending'" . " GROUP BY g.gedcom_name")->fetchOneColumn(); if ($changed_gedcoms) { $changes = Database::prepare("SELECT c.*, u.user_name, u.real_name, g.gedcom_name, new_gedcom, old_gedcom" . " FROM `##change` c" . " JOIN `##user` u USING (user_id)" . " JOIN `##gedcom` g USING (gedcom_id)" . " WHERE c.status='pending'" . " ORDER BY gedcom_id, c.xref, c.change_id")->fetchAll(); $output = '<br><br><table class="list_table">'; $prev_xref = null; $prev_gedcom_id = null; foreach ($changes as $change) { $tree = Tree::findById($change->gedcom_id); preg_match('/^0 @' . WT_REGEX_XREF . '@ (' . WT_REGEX_TAG . ')/', $change->old_gedcom . $change->new_gedcom, $match); switch ($match[1]) { case 'INDI': $record = new Individual($change->xref, $change->old_gedcom, $change->new_gedcom, $tree); break; case 'FAM': $record = new Family($change->xref, $change->old_gedcom, $change->new_gedcom, $tree); break; case 'SOUR': $record = new Source($change->xref, $change->old_gedcom, $change->new_gedcom, $tree); break; case 'REPO': $record = new Repository($change->xref, $change->old_gedcom, $change->new_gedcom, $tree); break; case 'OBJE':
/** * These functions provide access to hitcounter for use in the HTML block. * * @param string $page_name * @param string[] $params * * @return string */ private function hitCountQuery($page_name, $params) { if (is_array($params) && isset($params[0]) && $params[0] != '') { $page_parameter = $params[0]; } else { $page_parameter = ''; } if ($page_name === null) { // index.php?ctype=gedcom $page_name = 'index.php'; $page_parameter = 'gedcom:' . ($page_parameter ? Tree::findByName($page_parameter)->getTreeId() : $this->tree->getTreeId()); } elseif ($page_name == 'index.php') { // index.php?ctype=user $user = User::findByIdentifier($page_parameter); $page_parameter = 'user:'******'<span class="odometer">' . I18N::digits(HitCounter::getCount($this->tree, $page_name, $page_parameter)) . '</span>'; }
<?php echo I18N::translate('Where a user is associated to an individual record in a family tree and has a role of member, editor, or moderator, you can prevent them from accessing the details of distant, living relations. You specify the number of relationship steps that the user is allowed to see.'); ?> <?php echo I18N::translate('For example, if you specify a path length of 2, the individual will be able to see their grandson (child, child), their aunt (parent, sibling), their step-daughter (spouse, child), but not their first cousin (parent, sibling, child).'); ?> <?php echo I18N::translate('Note: longer path lengths require a lot of calculation, which can make your website run slowly for these users.'); ?> </p> </td> </tr> </thead> <tbody> <?php foreach (Tree::getAll() as $tree) { ?> <tr> <td> <?php echo $tree->getTitleHtml(); ?> </td> <td> <select name="canedit<?php echo $tree->getTreeId(); ?> "> <?php foreach ($ALL_EDIT_OPTIONS as $EDIT_OPTION => $desc) { ?>
/** * {@inhericDoc} * @see \MyArtJaub\Webtrees\Module\ModuleMenuItemInterface::getMenu() */ public function getMenu(Tree $tree, $reference = null) { $tree_url = $tree ? $tree->getNameUrl() : ''; return new Menu($this->getTitle(), 'module.php?mod=' . $this->getName() . '&mod_action=Certificate@listAll&ged=' . $tree_url, 'menu-maj-list-certificate', array('rel' => 'nofollow')); }
private function config() { global $WT_TREE; $controller = new webtrees\Controller\PageController(); $controller->restrictAccess(webtrees\Auth::isManager($WT_TREE))->setPageTitle($this->getTitle())->pageHeader(); $args = array(); $args['module_name'] = $this->getName(); $args['tree_id'] = $WT_TREE->getTreeId(); $items = webtrees\Database::prepare("SELECT block_id, block_order, gedcom_id, bs1.setting_value AS menu_title, bs2.setting_value AS menu_address" . " FROM `##block` b" . " JOIN `##block_setting` bs1 USING (block_id)" . " JOIN `##block_setting` bs2 USING (block_id)" . " WHERE module_name = :module_name" . " AND bs1.setting_name = 'menu_title'" . " AND bs2.setting_name = 'menu_address'" . " AND IFNULL(gedcom_id, :tree_id) = :tree_id" . " ORDER BY block_order")->execute($args)->fetchAll(); unset($args['tree_id']); $min_block_order = webtrees\Database::prepare("SELECT MIN(block_order) FROM `##block` WHERE module_name = :module_name")->execute($args)->fetchOne(); $max_block_order = webtrees\Database::prepare("SELECT MAX(block_order) FROM `##block` WHERE module_name = :module_name")->execute($args)->fetchOne(); ?> <style> .text-left-not-xs, .text-left-not-sm, .text-left-not-md, .text-left-not-lg { text-align: left; } .text-center-not-xs, .text-center-not-sm, .text-center-not-md, .text-center-not-lg { text-align: center; } .text-right-not-xs, .text-right-not-sm, .text-right-not-md, .text-right-not-lg { text-align: right; } .text-justify-not-xs, .text-justify-not-sm, .text-justify-not-md, .text-justify-not-lg { text-align: justify; } @media (max-width: 767px) { .text-left-not-xs, .text-center-not-xs, .text-right-not-xs, .text-justify-not-xs { text-align: inherit; } .text-left-xs { text-align: left; } .text-center-xs { text-align: center; } .text-right-xs { text-align: right; } .text-justify-xs { text-align: justify; } } @media (min-width: 768px) and (max-width: 991px) { .text-left-not-sm, .text-center-not-sm, .text-right-not-sm, .text-justify-not-sm { text-align: inherit; } .text-left-sm { text-align: left; } .text-center-sm { text-align: center; } .text-right-sm { text-align: right; } .text-justify-sm { text-align: justify; } } @media (min-width: 992px) and (max-width: 1199px) { .text-left-not-md, .text-center-not-md, .text-right-not-md, .text-justify-not-md { text-align: inherit; } .text-left-md { text-align: left; } .text-center-md { text-align: center; } .text-right-md { text-align: right; } .text-justify-md { text-align: justify; } } @media (min-width: 1200px) { .text-left-not-lg, .text-center-not-lg, .text-right-not-lg, .text-justify-not-lg { text-align: inherit; } .text-left-lg { text-align: left; } .text-center-lg { text-align: center; } .text-right-lg { text-align: right; } .text-justify-lg { text-align: justify; } } </style> <ol class="breadcrumb small"> <li><a href="admin.php"><?php echo webtrees\I18N::translate('Control panel'); ?> </a></li> <li><a href="admin_modules.php"><?php echo webtrees\I18N::translate('Module administration'); ?> </a></li> <li class="active"><?php echo $controller->getPageTitle(); ?> </li> </ol> <div class="row"> <div class="col-sm-4 col-xs-12"> <form class="form"> <label for="ged" class="sr-only"> <?php echo webtrees\I18N::translate('Family tree'); ?> </label> <input type="hidden" name="mod" value="<?php echo $this->getName(); ?> "> <input type="hidden" name="mod_action" value="admin_config"> <div class="col-sm-9 col-xs-9" style="padding:0;"> <?php echo webtrees\Functions\FunctionsEdit::selectEditControl('ged', webtrees\Tree::getNameList(), null, $WT_TREE->getName(), 'class="form-control"'); ?> </div> <div class="col-sm-3" style="padding:0;"> <input type="submit" class="btn btn-primary" value="<?php echo webtrees\I18N::translate('show'); ?> "> </div> </form> </div> <span class="visible-xs hidden-sm hidden-md hidden-lg" style="display:block;"></br></br></span> <div class="col-sm-4 text-center text-left-xs col-xs-12"> <p> <a href="module.php?mod=<?php echo $this->getName(); ?> &mod_action=admin_edit" class="btn btn-primary"> <i class="fa fa-plus"></i> <?php echo webtrees\I18N::translate('Add Menu'); ?> </a> </p> </div> <div class="col-sm-4 text-right text-left-xs col-xs-12"> <?php // TODO: Move to internal item/page if (file_exists(WT_MODULES_DIR . $this->getName() . '/readme.html')) { ?> <a href="<?php echo WT_MODULES_DIR . $this->getName(); ?> /readme.html" class="btn btn-info"> <i class="fa fa-newspaper-o"></i> <?php echo webtrees\I18N::translate('ReadMe'); ?> </a> <?php } ?> </div> </div> <table class="table table-bordered table-condensed"> <thead> <tr> <th class="col-sm-2"><?php echo webtrees\I18N::translate('Position'); ?> </th> <th class="col-sm-4"><?php echo webtrees\I18N::translate('Menu title'); ?> </th> <th class="col-sm-4"><?php echo webtrees\I18N::translate('Menu address'); ?> </th> <th class="col-sm-2" colspan=4><?php echo webtrees\I18N::translate('Controls'); ?> </th> </tr> </thead> <tbody> <?php foreach ($items as $item) { ?> <tr> <td> <?php echo $item->block_order, ', '; if ($item->gedcom_id == null) { echo webtrees\I18N::translate('All'); } else { echo webtrees\Tree::findById($item->gedcom_id)->getTitleHtml(); } ?> </td> <td> <?php echo webtrees\Filter::escapeHtml(webtrees\I18N::translate($item->menu_title)); ?> </td> <td> <?php echo webtrees\Filter::escapeHtml(substr(webtrees\I18N::translate($item->menu_address), 0, 1) == '<' ? webtrees\I18N::translate($item->menu_address) : nl2br(webtrees\I18N::translate($item->menu_address))); ?> </td> <td class="text-center"> <a href="module.php?mod=<?php echo $this->getName(); ?> &mod_action=admin_edit&block_id=<?php echo $item->block_id; ?> "> <div class="icon-edit"> </div> </a> </td> <td class="text-center"> <a href="module.php?mod=<?php echo $this->getName(); ?> &mod_action=admin_moveup&block_id=<?php echo $item->block_id; ?> "> <?php if ($item->block_order == $min_block_order) { echo ' '; } else { echo '<div class="icon-uarrow"> </div>'; } ?> </a> </td> <td class="text-center"> <a href="module.php?mod=<?php echo $this->getName(); ?> &mod_action=admin_movedown&block_id=<?php echo $item->block_id; ?> "> <?php if ($item->block_order == $max_block_order) { echo ' '; } else { echo '<div class="icon-darrow"> </div>'; } ?> </a> </td> <td class="text-center"> <a href="module.php?mod=<?php echo $this->getName(); ?> &mod_action=admin_delete&block_id=<?php echo $item->block_id; ?> " onclick="return confirm('<?php echo webtrees\I18N::translate('Are you sure you want to delete this menu?'); ?> ');"> <div class="icon-delete"> </div> </a> </td> </tr> <?php } ?> </tbody> </table> <?php }
echo '<p>', I18N::translate('In a family tree, each record has an internal reference number (called an “XREF”) such as “F123” or “R14”.'), '</p>', '<p>', I18N::plural('The two family trees have %1$s record which uses the same “XREF”.', 'The two family trees have %1$s records which use the same “XREF”.', count($xrefs), count($xrefs)), '</p>', '<p>', I18N::translate('You must renumber the records in one of the trees before you can merge them.'), '</p>', '<p>', '<a class="current" href="admin_trees_renumber.php?ged=', $tree1->getNameUrl(), '">', I18N::translate('Renumber family tree'), ' — ', $tree1->getTitleHtml(), '</a>', '</p>', '<p>', '<a class="current" href="admin_trees_renumber.php?ged=', $tree2->getNameUrl(), '">', I18N::translate('Renumber family tree'), ' — ', $tree2->getTitleHtml(), '</a>', '</p>'; } else { Database::beginTransaction(); Database::exec("LOCK TABLE" . " `##individuals` WRITE," . " `##individuals` AS individuals2 READ," . " `##families` WRITE," . " `##families` AS families2 READ," . " `##sources` WRITE," . " `##sources` AS sources2 READ," . " `##media` WRITE," . " `##media` AS media2 READ," . " `##other` WRITE," . " `##other` AS other2 READ," . " `##name` WRITE," . " `##name` AS name2 READ," . " `##placelinks` WRITE," . " `##placelinks` AS placelinks2 READ," . " `##change` WRITE," . " `##change` AS change2 READ," . " `##dates` WRITE," . " `##dates` AS dates2 READ," . " `##default_resn` WRITE," . " `##default_resn` AS default_resn2 READ," . " `##hit_counter` WRITE," . " `##hit_counter` AS hit_counter2 READ," . " `##link` WRITE," . " `##link` AS link2 READ"); Database::prepare("INSERT INTO `##individuals` (i_id, i_file, i_rin, i_sex, i_gedcom)" . " SELECT i_id, ?, i_rin, i_sex, i_gedcom FROM `##individuals` AS individuals2 WHERE i_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##families` (f_id, f_file, f_husb, f_wife, f_gedcom, f_numchil)" . " SELECT f_id, ?, f_husb, f_wife, f_gedcom, f_numchil FROM `##families` AS families2 WHERE f_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##sources` (s_id, s_file, s_name, s_gedcom)" . " SELECT s_id, ?, s_name, s_gedcom FROM `##sources` AS sources2 WHERE s_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##media` (m_id, m_ext, m_type, m_titl, m_filename, m_file, m_gedcom)" . " SELECT m_id, m_ext, m_type, m_titl, m_filename, ?, m_gedcom FROM `##media` AS media2 WHERE m_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom)" . " SELECT o_id, ?, o_type, o_gedcom FROM `##other` AS other2 WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##name` (n_file, n_id, n_num, n_type, n_sort, n_full, n_surname, n_surn, n_givn, n_soundex_givn_std, n_soundex_surn_std, n_soundex_givn_dm, n_soundex_surn_dm)" . " SELECT ?, n_id, n_num, n_type, n_sort, n_full, n_surname, n_surn, n_givn, n_soundex_givn_std, n_soundex_surn_std, n_soundex_givn_dm, n_soundex_surn_dm FROM `##name` AS name2 WHERE n_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##placelinks` (pl_p_id, pl_gid, pl_file)" . " SELECT pl_p_id, pl_gid, ? FROM `##placelinks` AS placelinks2 WHERE pl_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##dates` (d_day, d_month, d_mon, d_year, d_julianday1, d_julianday2, d_fact, d_gid, d_file, d_type)" . " SELECT d_day, d_month, d_mon, d_year, d_julianday1, d_julianday2, d_fact, d_gid, ?, d_type FROM `##dates` AS dates2 WHERE d_file = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##default_resn` (gedcom_id, xref, tag_type, resn)" . " SELECT ?, xref, tag_type, resn FROM `##default_resn` AS default_resn2 WHERE gedcom_id = ?")->execute(array($tree2_id, $tree1_id)); Database::prepare("INSERT INTO `##link` (l_file, l_from, l_type, l_to)" . " SELECT ?, l_from, l_type, l_to FROM `##link` AS link2 WHERE l_file = ?")->execute(array($tree2_id, $tree1_id)); // This table may contain old (deleted) references, which could clash. IGNORE these. Database::prepare("INSERT IGNORE INTO `##change` (change_time, status, gedcom_id, xref, old_gedcom, new_gedcom, user_id)" . " SELECT change_time, status, ?, xref, old_gedcom, new_gedcom, user_id FROM `##change` AS change2 WHERE gedcom_id = ?")->execute(array($tree2_id, $tree1_id)); // This table may contain old (deleted) references, which could clash. IGNORE these. Database::prepare("INSERT IGNORE INTO `##hit_counter` (gedcom_id, page_name, page_parameter, page_count)" . " SELECT ?, page_name, page_parameter, page_count FROM `##hit_counter` AS hit_counter2 WHERE gedcom_id = ? AND page_name <> 'index.php'")->execute(array($tree2_id, $tree1_id)); Database::exec("UNLOCK TABLES"); Database::commit(); echo '<p>', I18N::translate('The family trees have been merged successfully.'), '</p>'; } } else { echo '<form method="post">'; echo '<input type="hidden" name="go" value="1">'; echo '<p>', I18N::translate('Copy all the records from %1$s into %2$s.', FunctionsEdit::selectEditControl('tree1_id', Tree::getIdList(), '', null), FunctionsEdit::selectEditControl('tree2_id', Tree::getIdList(), '', null)), '</p>'; echo '<button type="submit" class="btn btn-primary">'; echo '<i class="fa fa-check"></i> ', I18N::translate('continue'); echo '</button>'; echo '</form>'; }
/** * Fetch a list of individuals with specified names * * To search for unknown names, use $surn="@N.N.", $salpha="@" or $galpha="@" * To search for names with no surnames, use $salpha="," * * @param Tree $tree only fetch individuals from this tree * @param string $surn if set, only fetch people with this surname * @param string $salpha if set, only fetch surnames starting with this letter * @param string $galpha if set, only fetch given names starting with this letter * @param bool $marnm if set, include married names * @param bool $fams if set, only fetch individuals with FAMS records * * @return Individual[] */ public static function individuals(Tree $tree, $surn, $salpha, $galpha, $marnm, $fams) { $sql = "SELECT i_id AS xref, i_gedcom AS gedcom, n_full " . "FROM `##individuals` " . "JOIN `##name` ON n_id = i_id AND n_file = i_file " . ($fams ? "JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") . "WHERE n_file = :tree_id " . ($marnm ? "" : "AND n_type != '_MARNM'"); $args = array('tree_id' => $tree->getTreeId()); if ($surn) { $sql .= " AND n_surn COLLATE :collate_1 = :surn"; $args['collate_1'] = I18N::collation(); $args['surn'] = $surn; } elseif ($salpha === ',') { $sql .= " AND n_surn = ''"; } elseif ($salpha === '@') { $sql .= " AND n_surn = '@N.N.'"; } elseif ($salpha) { $sql .= " AND " . self::getInitialSql('n_surn', $salpha); } else { // All surnames $sql .= " AND n_surn NOT IN ('', '@N.N.')"; } if ($galpha) { $sql .= " AND " . self::getInitialSql('n_givn', $galpha); } $sql .= " ORDER BY CASE n_surn WHEN '@N.N.' THEN 1 ELSE 0 END, n_surn COLLATE :collate_2, CASE n_givn WHEN '@P.N.' THEN 1 ELSE 0 END, n_givn COLLATE :collate_3"; $args['collate_2'] = I18N::collation(); $args['collate_3'] = I18N::collation(); $list = array(); $rows = Database::prepare($sql)->execute($args)->fetchAll(); foreach ($rows as $row) { $person = Individual::getInstance($row->xref, $tree, $row->gedcom); // The name from the database may be private - check the filtered list... foreach ($person->getAllNames() as $n => $name) { if ($name['fullNN'] == $row->n_full) { $person->setPrimaryName($n); // We need to clone $person, as we may have multiple references to the // same person in this list, and the "primary name" would otherwise // be shared amongst all of them. $list[] = clone $person; break; } } } return $list; }
/** * Print read-more link * * @param type $root * @return string */ private function printReadMoreLink($root) { return '<div id="read-more-link">' . '<a href="module.php?mod=' . $this->getName() . '&mod_action=page&rootid=' . $root . '&ged=' . Filter::escapeUrl(Tree::findById($this->tree_id)->getName()) . '">' . I18N::translate('Read more') . '</a>' . '</div>'; }