/** * Get a list of actual surnames and variants, based on a "root" surname. * * @param string $surn if set, only fetch people with this surname * @param string $salpha if set, only consider surnames starting with this letter * @param bool $marnm if set, include married names * @param bool $fams if set, only consider individuals with FAMS records * @param int $ged_id only consider individuals from this gedcom * * @return array */ public static function surnames($surn, $salpha, $marnm, $fams, $ged_id) { $sql = "SELECT SQL_CACHE n2.n_surn, n1.n_surname, n1.n_id" . " FROM `##name` n1 " . ($fams ? " JOIN `##link` ON (n_id=l_from AND n_file=l_file AND l_type='FAMS') " : "") . " JOIN (SELECT n_surn, n_file FROM `##name`" . " WHERE n_file={$ged_id}" . ($marnm ? "" : " AND n_type!='_MARNM'"); if ($surn) { $sql .= " AND n_surn COLLATE '" . WT_I18N::$collation . "' =" . WT_DB::quote($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.')"; } $sql .= " GROUP BY n_surn COLLATE '" . WT_I18N::$collation . "', n_file) n2 ON (n1.n_surn=n2.n_surn COLLATE '" . WT_I18N::$collation . "' AND n1.n_file=n2.n_file)"; if (!$marnm) { $sql .= " AND n_type!='_MARNM'"; } $list = array(); foreach (WT_DB::prepare($sql)->fetchAll() as $row) { $list[WT_I18N::strtoupper($row->n_surn)][$row->n_surname][$row->n_id] = true; } return $list; }
/** * Get array of common surnames * * This function returns a simple array of the most common surnames * found in the individuals list. * * @param int $min the number of times a surname must occur before it is added to the array * * @return array */ function get_common_surnames($min) { $COMMON_NAMES_ADD = get_gedcom_setting(WT_GED_ID, 'COMMON_NAMES_ADD'); $COMMON_NAMES_REMOVE = get_gedcom_setting(WT_GED_ID, 'COMMON_NAMES_REMOVE'); $topsurns = get_top_surnames(WT_GED_ID, $min, 0); foreach (explode(',', $COMMON_NAMES_ADD) as $surname) { if ($surname && !array_key_exists($surname, $topsurns)) { $topsurns[$surname] = $min; } } foreach (explode(',', $COMMON_NAMES_REMOVE) as $surname) { unset($topsurns[WT_I18N::strtoupper($surname)]); } //-- check if we found some, else recurse if (empty($topsurns) && $min > 2) { return get_common_surnames($min / 2); } else { uksort($topsurns, array('WT_I18N', 'strcasecmp')); foreach ($topsurns as $key => $value) { $topsurns[$key] = array('name' => $key, 'match' => $value); } return $topsurns; } }
private static function DMSoundex($name) { // Apply special transformation rules to the input string $name = WT_I18N::strtoupper($name); foreach (self::$transformNameTable as $transformRule) { $name = str_replace($transformRule[0], $transformRule[1], $name); } // Initialize $name_script = WT_I18N::textScript($name); if ($name_script == 'Hebr' || $name_script == 'Arab') { $noVowels = true; } else { $noVowels = false; } $lastPos = strlen($name) - 1; $currPos = 0; $state = 1; // 1: start of input string, 2: before vowel, 3: other $result = array(); // accumulate complete 6-digit D-M codes here $partialResult = array(); // accumulate incomplete D-M codes here $partialResult[] = array('!'); // initialize 1st partial result ('!' stops "duplicate sound" check) // Loop through the input string. // Stop when the string is exhausted or when no more partial results remain while (count($partialResult) != 0 && $currPos <= $lastPos) { // Find the DM coding table entry for the chunk at the current position $thisEntry = substr($name, $currPos, self::MAXCHAR); // Get maximum length chunk while ($thisEntry != '') { if (isset(self::$dmsounds[$thisEntry])) { break; } $thisEntry = substr($thisEntry, 0, -1); // Not in table: try a shorter chunk } if ($thisEntry == '') { $currPos++; // Not in table: advance pointer to next byte continue; // and try again } $soundTableEntry = self::$dmsounds[$thisEntry]; $workingResult = $partialResult; $partialResult = array(); $currPos += strlen($thisEntry); if ($state != 1) { // Not at beginning of input string if ($currPos <= $lastPos) { // Determine whether the next chunk is a vowel $nextEntry = substr($name, $currPos, self::MAXCHAR); // Get maximum length chunk while ($nextEntry != '') { if (isset(self::$dmsounds[$nextEntry])) { break; } $nextEntry = substr($nextEntry, 0, -1); // Not in table: try a shorter chunk } } else { $nextEntry = ''; } if ($nextEntry != '' && self::$dmsounds[$nextEntry][0] != '0') { $state = 2; } else { $state = 3; } } while ($state < count($soundTableEntry)) { if ($soundTableEntry[$state] == '') { // empty means 'ignore this sound in this state' foreach ($workingResult as $workingEntry) { $tempEntry = $workingEntry; $tempEntry[count($tempEntry) - 1] .= '!'; // Prevent false 'doubles' $partialResult[] = $tempEntry; } } else { foreach ($workingResult as $workingEntry) { if ($soundTableEntry[$state] !== $workingEntry[count($workingEntry) - 1]) { // Incoming sound isn't a duplicate of the previous sound $workingEntry[] = $soundTableEntry[$state]; } else { // Incoming sound is a duplicate of the previous sound // For Hebrew and Arabic, we need to create a pair of D-M sound codes, // one of the pair with only a single occurrence of the duplicate sound, // the other with both occurrences if ($noVowels) { //$partialResult[] = $workingEntry; $workingEntry[] = $soundTableEntry[$state]; } } if (count($workingEntry) < 7) { $partialResult[] = $workingEntry; } else { // This is the 6th code in the sequence // We're looking for 7 entries because the first is '!' and doesn't count $tempResult = str_replace('!', '', implode('', $workingEntry)); // Only return codes from recognisable sounds if ($tempResult) { $result[] = substr($tempResult . '000000', 0, 6); } } } } $state = $state + 3; // Advance to next triplet while keeping the same basic state } } // Zero-fill and copy all remaining partial results foreach ($partialResult as $workingEntry) { $tempResult = str_replace('!', '', implode('', $workingEntry)); // Only return codes from recognisable sounds if ($tempResult) { $result[] = substr($tempResult . '000000', 0, 6); } } return $result; }
function format_surname_list($surnames, $style, $totals, $script) { global $GEDCOM; $html = array(); foreach ($surnames as $surn => $surns) { // Each surname links back to the indilist if ($surn) { $url = $script . '?surname=' . urlencode($surn) . '&ged=' . rawurlencode($GEDCOM); } else { $url = $script . '?alpha=,&ged=' . rawurlencode($GEDCOM); } // If all the surnames are just case variants, then merge them into one // Comment out this block if you want SMITH listed separately from Smith $first_spfxsurn = null; foreach ($surns as $spfxsurn => $indis) { if ($first_spfxsurn) { if (WT_I18N::strtoupper($spfxsurn) == WT_I18N::strtoupper($first_spfxsurn)) { $surns[$first_spfxsurn] = array_merge($surns[$first_spfxsurn], $surns[$spfxsurn]); unset($surns[$spfxsurn]); } } else { $first_spfxsurn = $spfxsurn; } } $subhtml = '<a href="' . $url . '" dir="auto">' . WT_Filter::escapeHtml(implode(WT_I18N::$list_separator, array_keys($surns))) . '</a>'; if ($totals) { $subtotal = 0; foreach ($surns as $indis) { $subtotal += count($indis); } $subhtml .= ' (' . WT_I18N::number($subtotal) . ')'; } $html[] = $subhtml; } switch ($style) { case 1: return '<ul><li>' . implode('</li><li>', $html) . '</li></ul>'; case 2: return implode(WT_I18N::$list_separator, $html); case 3: $i = 0; $count = count($html); $col = 1; if ($count > 36) { $col = 4; } else { if ($count > 18) { $col = 3; } else { if ($count > 6) { $col = 2; } } } $newcol = ceil($count / $col); $html2 = '<table class="list_table"><tr>'; $html2 .= '<td class="list_value" style="padding: 14px;">'; foreach ($html as $surns) { $html2 .= $surns . '<br>'; $i++; if ($i == $newcol && $i < $count) { $html2 .= '</td><td class="list_value" style="padding: 14px;">'; $newcol = $i + ceil($count / $col); } } $html2 .= '</td></tr></table>'; return $html2; } }
public function getBlock($block_id, $template = true, $cfg = null) { global $ctype, $SURNAME_LIST_STYLE; require_once WT_ROOT . 'includes/functions/functions_print_lists.php'; $COMMON_NAMES_REMOVE = get_gedcom_setting(WT_GED_ID, 'COMMON_NAMES_REMOVE'); $COMMON_NAMES_THRESHOLD = get_gedcom_setting(WT_GED_ID, 'COMMON_NAMES_THRESHOLD'); $num = get_block_setting($block_id, 'num', 10); $infoStyle = get_block_setting($block_id, 'infoStyle', 'table'); $block = get_block_setting($block_id, 'block', false); if ($cfg) { foreach (array('num', 'infoStyle', 'block') as $name) { if (array_key_exists($name, $cfg)) { ${$name} = $cfg[$name]; } } } // This next function is a bit out of date, and doesn't cope well with surname variants $top_surnames = get_top_surnames(WT_GED_ID, $COMMON_NAMES_THRESHOLD, $num); // Remove names found in the "Remove Names" list if ($COMMON_NAMES_REMOVE) { foreach (preg_split("/[,; ]+/", $COMMON_NAMES_REMOVE) as $delname) { unset($top_surnames[$delname]); unset($top_surnames[WT_I18N::strtoupper($delname)]); } } $all_surnames = array(); $i = 0; foreach (array_keys($top_surnames) as $top_surname) { $all_surnames = array_merge($all_surnames, WT_Query_Name::surnames($top_surname, '', false, false, WT_GED_ID)); if (++$i == $num) { break; } } if ($i < $num) { $num = $i; } $id = $this->getName() . $block_id; $class = $this->getName() . '_block'; if ($ctype == 'gedcom' && WT_USER_GEDCOM_ADMIN || $ctype == 'user' && WT_USER_ID) { $title = '<i class="icon-admin" title="' . WT_I18N::translate('Configure') . '" onclick="modalDialog(\'block_edit.php?block_id=' . $block_id . '\', \'' . $this->getTitle() . '\');"></i>'; } else { $title = ''; } if ($num == 1) { // I18N: i.e. most popular surname. $title .= WT_I18N::translate('Top surname'); } else { // I18N: Title for a list of the most common surnames, %s is a number. Note that a separate translation exists when %s is 1 $title .= WT_I18N::plural('Top %s surname', 'Top %s surnames', $num, WT_I18N::number($num)); } switch ($infoStyle) { case 'tagcloud': uksort($all_surnames, array('WT_I18N', 'strcasecmp')); $content = format_surname_tagcloud($all_surnames, 'indilist.php', true); break; case 'list': uasort($all_surnames, array('top10_surnames_WT_Module', 'top_surname_sort')); $content = format_surname_list($all_surnames, '1', true, 'indilist.php'); break; case 'array': uasort($all_surnames, array('top10_surnames_WT_Module', 'top_surname_sort')); $content = format_surname_list($all_surnames, '2', true, 'indilist.php'); break; case 'table': default: uasort($all_surnames, array('top10_surnames_WT_Module', 'top_surname_sort')); $content = format_surname_table($all_surnames, 'indilist.php'); break; } if ($template) { if ($block) { require WT_THEME_DIR . 'templates/block_small_temp.php'; } else { require WT_THEME_DIR . 'templates/block_main_temp.php'; } } else { return $content; } }
function chartCommonSurnames($params = null) { global $WT_STATS_CHART_COLOR1, $WT_STATS_CHART_COLOR2, $WT_STATS_S_CHART_X, $WT_STATS_S_CHART_Y; if ($params === null) { $params = array(); } if (isset($params[0]) && $params[0] != '') { $size = strtolower($params[0]); } else { $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y; } if (isset($params[1]) && $params[1] != '') { $color_from = strtolower($params[1]); } else { $color_from = $WT_STATS_CHART_COLOR1; } if (isset($params[2]) && $params[2] != '') { $color_to = strtolower($params[2]); } else { $color_to = $WT_STATS_CHART_COLOR2; } if (isset($params[3]) && $params[3] != '') { $threshold = strtolower($params[3]); } else { $threshold = get_gedcom_setting($this->_ged_id, 'COMMON_NAMES_THRESHOLD'); } if (isset($params[4]) && $params[4] != '') { $maxtoshow = strtolower($params[4]); } else { $maxtoshow = 7; } $sizes = explode('x', $size); $tot_indi = $this->_totalIndividuals(); $surnames = get_common_surnames($threshold); if (count($surnames) <= 0) { return ''; } $SURNAME_TRADITION = get_gedcom_setting(WT_GED_ID, 'SURNAME_TRADITION'); uasort($surnames, array('WT_Stats', '_name_total_rsort')); $surnames = array_slice($surnames, 0, $maxtoshow); $all_surnames = array(); foreach (array_keys($surnames) as $n => $surname) { if ($n >= $maxtoshow) { break; } $all_surnames = array_merge($all_surnames, WT_Query_Name::surnames(WT_I18N::strtoupper($surname), '', false, false, WT_GED_ID)); } $tot = 0; foreach ($surnames as $surname) { $tot += $surname['match']; } $chd = ''; $chl = array(); foreach ($all_surnames as $surns) { $count_per = 0; $max_name = 0; foreach ($surns as $spfxsurn => $indis) { $per = count($indis); $count_per += $per; // select most common surname from all variants if ($per > $max_name) { $max_name = $per; $top_name = $spfxsurn; } } switch ($SURNAME_TRADITION) { case 'polish': // most common surname should be in male variant (Kowalski, not Kowalska) $top_name = preg_replace(array('/ska$/', '/cka$/', '/dzka$/', '/żka$/'), array('ski', 'cki', 'dzki', 'żki'), $top_name); } $per = round(100 * $count_per / $tot_indi, 0); $chd .= self::_array_to_extended_encoding($per); //ToDo: RTL names are often printed LTR when also LTR names are present $chl[] = $top_name . ' - ' . WT_I18N::number($count_per); } $per = round(100 * ($tot_indi - $tot) / $tot_indi, 0); $chd .= self::_array_to_extended_encoding($per); $chl[] = WT_I18N::translate('Other') . ' - ' . WT_I18N::number($tot_indi - $tot); $chart_title = implode(WT_I18N::$list_separator, $chl); $chl = implode('|', $chl); return '<img src="https://chart.googleapis.com/chart?cht=p3&chd=e:' . $chd . '&chs=' . $size . '&chco=' . $color_from . ',' . $color_to . '&chf=bg,s,ffffff00&chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '" />'; }