$v) { if (!preg_match_recursive($regex, $v)) { return false; } } return true; } else { // Neither scalar nor array. Object? return false; } } } function trim_recursive($var) { if (is_scalar($var)) { return trim($var); } else { if (is_array($var)) { foreach ($var as $k=>$v) { $var[$k]=trim_recursive($v); } return $var; } else { // Neither scalar nor array. Object? return $var; } } } // Update the variable definitions in a PHP config file, such as config.php function update_config(&$text, $var, $value) { // NULL values probably wouldn't hurt, but empty strings are probably better if (is_null($value)) { $value=''; } $regex='/^[ \t]*[$]'.$var.'[ \t]*=.*;[ \t]*/m'; $assign='$'.$var.'='.var_export($value, true).'; '; if (preg_match($regex, $text)) { // Variable found in file - update it $text=preg_replace($regex, $assign, $text); } else { // Variable not found in file - insert it $text=preg_replace('/^(.*[\r\n]+)([ \t]*[$].*)$/s', '$1'.$assign." // new config variable\n".'$2', $text); } } // Convert a file upload PHP error code into user-friendly text function file_upload_error_text($error_code) { switch ($error_code) { case UPLOAD_ERR_OK: return i18n::translate('File successfully uploaded'); case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: return i18n::translate('Uploaded file exceeds the allowed size'); case UPLOAD_ERR_PARTIAL: return i18n::translate('File was only partially uploaded, please try again'); case UPLOAD_ERR_NO_FILE: return i18n::translate('No file was received. Please upload again.'); case UPLOAD_ERR_NO_TMP_DIR: return i18n::translate('Missing PHP temporary directory'); case UPLOAD_ERR_CANT_WRITE: return i18n::translate('PHP failed to write to disk'); case UPLOAD_ERR_EXTENSION: return i18n::translate('PHP blocked file by extension'); } } /** * get gedcom configuration file * * this function returns the path to the currently active GEDCOM configuration file * @return string path to gedcom.ged_conf.php configuration file */ function get_config_file($ged_id=WT_GED_ID) { global $INDEX_DIRECTORY; $config=get_gedcom_setting($ged_id, 'config'); // Compatibility with non-php based storage, (PGV 4.3.0 onwards) $config=str_replace('${INDEX_DIRECTORY}', $INDEX_DIRECTORY, $config); if (!file_exists($config)) { return 'config_gedcom.php'; } else { return $config; } } /** * print write_access option * * @param string $checkVar */ function write_access_option($checkVar) { echo "\n"; echo "\n"; echo "\n"; echo "\n"; } /** * Get the path to the privacy file * * Get the path to the privacy file for the currently active GEDCOM * @return string path to the privacy file */ function get_privacy_file($ged_id=WT_GED_ID) { global $INDEX_DIRECTORY; $privfile=get_gedcom_setting($ged_id, 'privacy'); // Compatibility with non-php based storage $privfile=str_replace('${INDEX_DIRECTORY}', $INDEX_DIRECTORY, $privfile); if (!file_exists($privfile)) { return 'privacy.php'; } else { return $privfile; } } function load_privacy_file($ged_id=WT_GED_ID) { // Load the privacy settings into global scope global $SHOW_DEAD_PEOPLE, $SHOW_LIVING_NAMES, $SHOW_SOURCES, $MAX_ALIVE_AGE; global $ENABLE_CLIPPINGS_CART, $SHOW_MULTISITE_SEARCH; global $USE_RELATIONSHIP_PRIVACY, $MAX_RELATION_PATH_LENGTH, $CHECK_MARRIAGE_RELATIONS; global $PRIVACY_BY_YEAR, $PRIVACY_BY_RESN, $SHOW_PRIVATE_RELATIONSHIPS; global $person_privacy, $user_privacy, $global_facts, $person_facts; // Load default settings require WT_ROOT.'privacy.php'; // Load settings for the specified gedcom $privacy_file=get_privacy_file($ged_id); if ($privacy_file && file_exists($privacy_file)) { require $privacy_file; } } /** * Update the site configuration settings * New settings are passed in as an array of key value pairs * The key in the array should be the name of the setting to change * the value should be the new value * $newconfig['CONFIGURED'] = true; * * @param array $newconfig * @param boolean $return return the text or try to write the file * @return mixed returns true on success, or returns an array of error messages on failure */ function update_site_config($newconfig, $return = false) { $errors = array(); //-- load the configuration file text if (file_exists("config.php")) { $configtext = file_get_contents("config.php"); } else { $configtext = file_get_contents("config.dist"); } foreach($newconfig as $setting=>$value) { update_config($configtext, $setting, $value); } //-- check if the configtext is valid PHP $res = @eval($configtext); if ($res===false) { if ($return) { return $configtext; } $fp = @fopen("config.php", "wb"); if (!$fp) { $error['msg'] = i18n::translate('Error!!! Cannot write to the webtrees configuration file. Please check file and directory permissions and try again.'); $errors[] = $error; } else { fwrite($fp, $configtext); fclose($fp); } } else { $error['msg'] = "There was an error in the generated config.php. ".htmlentities($configtext); $errors[] = $error; } if (count($errors)>0) { return $errors; } return true; } // This functions checks if an existing file is physically writeable // The standard PHP function only checks for the R/O attribute and doesn't // detect authorisation by ACL. function file_is_writeable($file) { $err_write = false; $handle = @fopen($file, "r+"); if ($handle) { $i = fclose($handle); $err_write = true; } return($err_write); } /** * Webtrees Error Handling function * * This function will be called by PHP whenever an error occurs. The error handling * is set in the session.php * @see http://us2.php.net/manual/en/function.set-error-handler.php */ function wt_error_handler($errno, $errstr, $errfile, $errline) { if ((error_reporting() > 0)&&($errno<2048)) { if (WT_ERROR_LEVEL==0) { return; } if (stristr($errstr, "by reference")==true) { return; } $fmt_msg="\n
ERROR {$errno}: {$errstr}
\n"; $log_msg="ERROR {$errno}: {$errstr};"; // Although debug_backtrace should always exist in PHP5, without this check, PHP sometimes crashes. // Possibly calling it generates an error, which causes infinite recursion?? if ($errno<16 && function_exists("debug_backtrace") && strstr($errstr, "headers already sent by")===false) { $backtrace=debug_backtrace(); $num=count($backtrace); if (WT_ERROR_LEVEL==1) { $num=1; } for ($i=0; $i<$num; $i++) { if ($i==0) { $fmt_msg.="0 Error occurred on "; $log_msg.="\n0 Error occurred on "; } else { $fmt_msg.="{$i} called from "; $log_msg.="\n{$i} called from "; } if (isset($backtrace[$i]["line"]) && isset($backtrace[$i]["file"])) { $fmt_msg.="line {$backtrace[$i]['line']} of file ".basename($backtrace[$i]['file']).""; $log_msg.="line {$backtrace[$i]['line']} of file ".basename($backtrace[$i]['file']); } if ($i<$num-1) { $fmt_msg.=" in function ".$backtrace[$i+1]['function'].""; $log_msg.=" in function ".$backtrace[$i+1]['function']; } $fmt_msg.="
\n"; } } echo $fmt_msg; if (function_exists('AddToLog')) { AddToLog($log_msg, 'error'); } if ($errno==1) { die(); } } return false; } // ************************************************* START OF GEDCOM FUNCTIONS ********************************* // /** * Get first tag in GEDCOM sub-record * * This routine uses function get_sub_record to retrieve the specified sub-record * and then returns the first tag. * */ function get_first_tag($level, $tag, $gedrec, $num=1) { $temp = get_sub_record($level, $level." ".$tag, $gedrec, $num)."\n"; $temp = str_replace("\r\n", "\n", $temp); $length = strpos($temp, "\n"); if ($length===false) { $length = strlen($temp); } return substr($temp, 2, $length-2); } /** * get a gedcom subrecord * * searches a gedcom record and returns a subrecord of it. A subrecord is defined starting at a * line with level N and all subsequent lines greater than N until the next N level is reached. * For example, the following is a BIRT subrecord: * 1 BIRT * 2 DATE 1 JAN 1900 * 2 PLAC Phoenix, Maricopa, Arizona * The following example is the DATE subrecord of the above BIRT subrecord: * 2 DATE 1 JAN 1900 * @author John Finlay (yalnifj) * @author Roland Dalmulder (roland-d) * @param int $level the N level of the subrecord to get * @param string $tag a gedcom tag or string to search for in the record (ie 1 BIRT or 2 DATE) * @param string $gedrec the parent gedcom record to search in * @param int $num this allows you to specify which matching $tag to get. Oftentimes a * gedcom record will have more that 1 of the same type of subrecord. An individual may have * multiple events for example. Passing $num=1 would get the first 1. Passing $num=2 would get the * second one, etc. * @return string the subrecord that was found or an empty string "" if not found. */ function get_sub_record($level, $tag, $gedrec, $num=1) { if (empty($gedrec)) { return ""; } // -- adding \n before and after gedrec $gedrec = "\n".$gedrec."\n"; $pos1=0; $subrec = ""; $tag = trim($tag); $searchTarget = "~[\r\n]".$tag."[\s]~"; $ct = preg_match_all($searchTarget, $gedrec, $match, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); if ($ct==0) { $tag = preg_replace('/(\w+)/', "_$1", $tag); $ct = preg_match_all($searchTarget, $gedrec, $match, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); if ($ct==0) { return ""; } } if ($ct<$num) { return ""; } $pos1 = $match[$num-1][0][1]; $pos2 = strpos($gedrec, "\n$level", $pos1+1); if (!$pos2) { $pos2 = strpos($gedrec, "\n1", $pos1+1); } if (!$pos2) { $pos2 = strpos($gedrec, "\nWT_", $pos1+1); // WT_SPOUSE, WT_FAMILY_ID ... } if (!$pos2) { return ltrim(substr($gedrec, $pos1)); } $subrec = substr($gedrec, $pos1, $pos2-$pos1); return ltrim($subrec); } /** * find all of the level 1 subrecords of the given record * @param string $gedrec the gedcom record to get the subrecords from * @param string $ignore a list of tags to ignore * @param boolean $families whether to include any records from the family * @param boolean $sort whether or not to sort the record by date * @param boolean $ApplyPriv whether to apply privacy right now or later * @return array an array of the raw subrecords to return */ function get_all_subrecords($gedrec, $ignore="", $families=true, $ApplyPriv=true) { global $GEDCOM; $ged_id=get_id_from_gedcom($GEDCOM); $repeats = array(); $id = ""; $gt = preg_match('/0 @('.WT_REGEX_XREF.')@/', $gedrec, $gmatch); if ($gt > 0) { $id = $gmatch[1]; } $hasResn = strstr($gedrec, " RESN "); $prev_tags = array(); $ct = preg_match_all('/\n1 ('.WT_REGEX_TAG.')(.*)/', $gedrec, $match, PREG_SET_ORDER|PREG_OFFSET_CAPTURE); for ($i=0; $i<$ct; $i++) { $fact = trim($match[$i][1][0]); $pos1 = $match[$i][0][1]; if ($i<$ct-1) { $pos2 = $match[$i+1][0][1]; } else { $pos2 = strlen($gedrec); } if (empty($ignore) || strpos($ignore, $fact)===false) { if (!$ApplyPriv || (showFact($fact, $id)&& showFactDetails($fact, $id))) { if (isset($prev_tags[$fact])) { $prev_tags[$fact]++; } else { $prev_tags[$fact] = 1; } $subrec = substr($gedrec, $pos1, $pos2-$pos1); if (!$ApplyPriv || !$hasResn || !FactViewRestricted($id, $subrec)) { if ($fact=="EVEN") { $tt = preg_match("/2 TYPE (.*)/", $subrec, $tmatch); if ($tt>0) { $type = trim($tmatch[1]); if (!$ApplyPriv || (showFact($type, $id)&&showFactDetails($type, $id))) { $repeats[] = trim($subrec)."\n"; } } else $repeats[] = trim($subrec)."\n"; } else { $repeats[] = trim($subrec)."\n"; } } } } } //-- look for any records in FAMS records if ($families) { $ft = preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $gedrec, $fmatch, PREG_SET_ORDER); for ($f=0; $f<$ft; $f++) { $famid = $fmatch[$f][1]; $famrec = find_family_record($fmatch[$f][1], $ged_id); $parents = find_parents_in_record($famrec); if ($id==$parents["HUSB"]) { $spid = $parents["WIFE"]; } else { $spid = $parents["HUSB"]; } $prev_tags = array(); $ct = preg_match_all('/\n1 ('.WT_REGEX_TAG.')(.*)/', $famrec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { $fact = trim($match[$i][1]); if (empty($ignore) || strpos($ignore, $fact)===false) { if (!$ApplyPriv || (showFact($fact, $id)&&showFactDetails($fact, $id))) { if (isset($prev_tags[$fact])) { $prev_tags[$fact]++; } else { $prev_tags[$fact] = 1; } $subrec = get_sub_record(1, "1 $fact", $famrec, $prev_tags[$fact]); $subrec .= "\n2 _PGVS @$spid@\n2 _PGVFS @$famid@\n"; if ($fact=="EVEN") { $ct = preg_match("/2 TYPE (.*)/", $subrec, $tmatch); if ($ct>0) { $type = trim($tmatch[1]); if (!$ApplyPriv or (showFact($type, $id)&&showFactDetails($type, $id))) { $repeats[] = trim($subrec)."\n"; } } else { $repeats[] = trim($subrec)."\n"; } } else { $repeats[] = trim($subrec)."\n"; } } } } } } return $repeats; } /** * get gedcom tag value * * returns the value of a gedcom tag from the given gedcom record * @param string $tag The tag to find, use : to delineate subtags * @param int $level The gedcom line level of the first tag to find, setting level to 0 will cause it to use 1+ the level of the incoming record * @param string $gedrec The gedcom record to get the value from * @param int $truncate Should the value be truncated to a certain number of characters * @param boolean $convert Should data like dates be converted using the configuration settings * @return string */ function get_gedcom_value($tag, $level, $gedrec, $truncate='', $convert=true) { global $SHOW_PEDIGREE_PLACES, $GEDCOM; $ged_id=get_id_from_gedcom($GEDCOM); if (empty($gedrec)) { return ""; } $tags = explode(':', $tag); $origlevel = $level; if ($level==0) { $level = $gedrec{0} + 1; } $subrec = $gedrec; foreach ($tags as $indexval => $t) { $lastsubrec = $subrec; $subrec = get_sub_record($level, "$level $t", $subrec); if (empty($subrec) && $origlevel==0) { $level--; $subrec = get_sub_record($level, "$level $t", $lastsubrec); } if (empty($subrec)) { if ($t=="TITL") { $subrec = get_sub_record($level, "$level ABBR", $lastsubrec); if (!empty($subrec)) { $t = "ABBR"; } } if (empty($subrec)) { if ($level>0) { $level--; } $subrec = get_sub_record($level, "@ $t", $gedrec); if (empty($subrec)) { return; } } } $level++; } $level--; $ct = preg_match("/$level $t(.*)/", $subrec, $match); if ($ct==0) { $ct = preg_match("/$level @.+@ (.+)/", $subrec, $match); } if ($ct==0) { $ct = preg_match("/@ $t (.+)/", $subrec, $match); } if ($ct > 0) { $value = trim($match[1]); $ct = preg_match("/@(.*)@/", $value, $match); if (($ct > 0 ) && ($t!="DATE")) { $oldsub = $subrec; switch ($t) { case 'HUSB': case 'WIFE': case 'CHIL': $subrec = find_person_record($match[1], $ged_id); break; case 'FAMC': case 'FAMS': $subrec = find_family_record($match[1], $ged_id); break; case 'SOUR': $subrec = find_source_record($match[1], $ged_id); break; case 'REPO': $subrec = find_other_record($match[1], $ged_id); break; default: $subrec = find_gedcom_record($match[1], $ged_id); break; } if ($subrec) { $value=$match[1]; $ct = preg_match("/0 @$match[1]@ $t (.+)/", $subrec, $match); if ($ct>0) { $value = $match[1]; $level = 0; } else $subrec = $oldsub; } else //-- set the value to the id without the @ $value = $match[1]; } if ($level!=0 || $t!="NOTE") { $value .= get_cont($level+1, $subrec); } $value = preg_replace("'\n'", "", $value); $value = preg_replace("'
'", "\n", $value); $value = trim($value); //-- if it is a date value then convert the date if ($convert && $t=="DATE") { $g = new GedcomDate($value); $value = $g->Display(); if (!empty($truncate)) { if (utf8_strlen($value)>$truncate) { $value = preg_replace("/\(.+\)/", "", $value); //if (utf8_strlen($value)>$truncate) { // $value = preg_replace_callback("/([a-zśź]+)/ui", create_function('$matches', 'return utf8_substr($matches[1], 0, 3);'), $value); //} } } } else //-- if it is a place value then apply the pedigree place limit if ($convert && $t=="PLAC") { if ($SHOW_PEDIGREE_PLACES>0) { $plevels = explode(',', $value); $value = ""; for ($plevel=0; $plevel<$SHOW_PEDIGREE_PLACES; $plevel++) { if (!empty($plevels[$plevel])) { if ($plevel>0) { $value .= ", "; } $value .= trim($plevels[$plevel]); } } } if (!empty($truncate)) { if (strlen($value)>$truncate) { $plevels = explode(',', $value); $value = ""; for ($plevel=0; $plevel0) { $value .= ", "; } $value .= trim($plevels[$plevel]); } else break; } } } } } else if ($convert && $t=="SEX") { if ($value=="M") { $value = utf8_substr(i18n::translate('Male'), 0, 1); } elseif ($value=="F") { $value = utf8_substr(i18n::translate('Female'), 0, 1); } else { $value = utf8_substr(i18n::translate('unknown'), 0, 1); } } else { if (!empty($truncate)) { if (strlen($value)>$truncate) { $plevels = explode(' ', $value); $value = ""; for ($plevel=0; $plevel0) { $value .= " "; } $value .= trim($plevels[$plevel]); } else break; } } } } } return $value; } return ""; } /** * create CONT lines * * Break input GEDCOM subrecord into pieces not more than 255 chars long, * with CONC and CONT lines as needed. Routine also pays attention to the * word wrapped Notes option. Routine also avoids splitting UTF-8 encoded * characters between lines. * * @param string $newline Input GEDCOM subrecord to be worked on * @return string $newged Output string with all necessary CONC and CONT lines */ function breakConts($newline) { global $WORD_WRAPPED_NOTES; // Determine level number of CONC and CONT lines $level = substr($newline, 0, 1); $tag = substr($newline, 1, 6); if ($tag!=" CONC " && $tag!=" CONT ") { $level ++; } $newged = ""; $newlines = preg_split("/\r?\n/", rtrim(stripLRMRLM($newline))); for ($k=0; $k0) { $newlines[$k] = "{$level} CONT ".$newlines[$k]; } if (strlen($newlines[$k])>255) { if ($WORD_WRAPPED_NOTES) { while (strlen($newlines[$k])>255) { // Make sure this piece ends on a blank, because one blank will be // added automatically when everything is put back together $lastBlank = strrpos(substr($newlines[$k], 0, 255), " "); $thisPiece = rtrim(substr($newlines[$k], 0, $lastBlank+1)); $newged .= $thisPiece."\n"; $newlines[$k] = substr($newlines[$k], (strlen($thisPiece)+1)); $newlines[$k] = "{$level} CONC ".$newlines[$k]; } } else { while (strlen($newlines[$k])>255) { // Make sure this piece doesn't end on a blank // (Blanks belong at the start of the next piece) $thisPiece = rtrim(substr($newlines[$k], 0, 255)); // Make sure this piece doesn't end in the middle of a UTF-8 character $nextPieceFirstChar = substr($newlines[$k], strlen($thisPiece), 1); if (($nextPieceFirstChar&"\xC0") == "\x80") { // Include all of the UTF-8 character in next piece while (true) { // Find the start of the UTF-8 encoded character $nextPieceFirstChar = substr($thisPiece, -1); $thisPiece = substr($thisPiece, 0, -1); if (($nextPieceFirstChar&"\xC0") != "\x80") { break; } } // Make sure we didn't back up to a blank $thisPiece = rtrim($thisPiece); } $newged .= $thisPiece."\n"; $newlines[$k] = substr($newlines[$k], strlen($thisPiece)); $newlines[$k] = "{$level} CONC ".$newlines[$k]; } } $newged .= trim($newlines[$k])."\n"; } else { $newged .= trim($newlines[$k])."\n"; } } return $newged; } /** * get CONT lines * * get the N+1 CONT or CONC lines of a gedcom subrecord * @param int $nlevel the level of the CONT lines to get * @param string $nrec the gedcom subrecord to search in * @return string a string with all CONT or CONC lines merged */ function get_cont($nlevel, $nrec, $tobr=true) { global $WORD_WRAPPED_NOTES; $text = ""; if ($tobr) { $newline = "
"; } else { $newline = "\n"; } $subrecords = explode("\n", $nrec); foreach ($subrecords as $thisSubrecord) { if (substr($thisSubrecord, 0, 2)!=$nlevel." ") { continue; } $subrecordType = substr($thisSubrecord, 2, 4); if ($subrecordType=="CONT") { $text .= $newline; } if ($subrecordType=="CONC" && $WORD_WRAPPED_NOTES) { $text .= " "; } if ($subrecordType=="CONT" || $subrecordType=="CONC") { $text .= rtrim(substr($thisSubrecord, 7)); } } return rtrim($text, " "); } /** * find the parents in a family * * find and return a two element array containing the parents of the given family record * @author John Finlay (yalnifj) * @param string $famid the gedcom xref id for the family * @return array returns a two element array with indexes HUSB and WIFE for the parent ids */ function find_parents($famid) { $famrec = find_gedcom_record($famid, WT_GED_ID, WT_USER_CAN_EDIT); if (empty($famrec)) { return false; } return find_parents_in_record($famrec); } /** * find the parents in a family record * * find and return a two element array containing the parents of the given family record * @author John Finlay (yalnifj) * @param string $famrec the gedcom record of the family to search in * @return array returns a two element array with indexes HUSB and WIFE for the parent ids */ function find_parents_in_record($famrec) { if (empty($famrec)) { return false; } $parents = array(); $ct = preg_match('/1 HUSB @('.WT_REGEX_XREF.')@/', $famrec, $match); if ($ct>0) { $parents["HUSB"]=$match[1]; } else { $parents["HUSB"]=""; } $ct = preg_match('/1 WIFE @('.WT_REGEX_XREF.')@/', $famrec, $match); if ($ct>0) { $parents["WIFE"]=$match[1]; } else { $parents["WIFE"]=""; } return $parents; } /** * find the children in a family * * find and return an array containing the children of the given family record * @author John Finlay (yalnifj) * @param string $famid the gedcom xref id for the family * @param string $me an xref id of a child to ignore, useful when you want to get a person's * siblings but do want to include them as well * @return array */ function find_children($famid, $me='') { $famrec = find_gedcom_record($famid, WT_GED_ID, true); if (empty($famrec)) { return false; } return find_children_in_record($famrec); } /** * find the children in a family record * * find and return an array containing the children of the given family record * @author John Finlay (yalnifj) * @param string $famrec the gedcom record of the family to search in * @param string $me an xref id of a child to ignore, useful when you want to get a person's * siblings but do want to include them as well * @return array */ function find_children_in_record($famrec, $me='') { $children = array(); if (empty($famrec)) { return $children; } $num = preg_match_all('/\n1 CHIL @('.WT_REGEX_XREF.')@/', $famrec, $match, PREG_SET_ORDER); for ($i=0; $i<$num; $i++) { $child = trim($match[$i][1]); if ($child!=$me) { $children[] = $child; } } return $children; } /** * find all child family ids * * searches an individual gedcom record and returns an array of the FAMC ids where this person is a * child in the family, but only those families that are allowed to be seen by current user * @param string $pid the gedcom xref id for the person to look in * @return array array of family ids */ function find_family_ids($pid) { $indirec=find_person_record($pid, WT_GED_ID); return find_visible_families_in_record($indirec, "FAMC"); } /** * find all spouse family ids * * searches an individual gedcom record and returns an array of the FAMS ids where this person is a * spouse in the family, but only those families that are allowed to be seen by current user * @param string $pid the gedcom xref id for the person to look in * @return array array of family ids */ function find_sfamily_ids($pid) { $indirec=find_person_record($pid, WT_GED_ID); return find_visible_families_in_record($indirec, "FAMS"); } /** * find all family ids in the given record * * searches an individual gedcom record and returns an array of the FAMS|C ids * @param string $indirec the gedcom record for the person to look in * @param string $tag The family tag to look for * @return array array of family ids */ function find_families_in_record($indirec, $tag) { preg_match_all("/\n1 {$tag} @(".WT_REGEX_XREF.')@/', $indirec, $match); return $match[1]; } /** * find all family ids in the given record that should be visible to the current user * * searches an individual gedcom record and returns an array of the FAMS|C ids that are visible * @param string $indirec the gedcom record for the person to look in * @param string $tag The family tag to look for, FAMS or FAMC * @return array array of family ids */ function find_visible_families_in_record($indirec, $tag) { $allfams = find_families_in_record($indirec, $tag); $visiblefams = array(); // select only those that are visible to current user foreach ($allfams as $key=>$famid) { if (displayDetailsById($famid, "FAM")) { $visiblefams[] = $famid; } } return $visiblefams; } // ************************************************* START OF MULTIMEDIA FUNCTIONS ********************************* // /** * find the highlighted media object for a gedcom entity * 1. Ignore all media objects that are not displayable because of Privacy rules * 2. Ignore all media objects with the Highlight option set to "N" * 3. Pick the first media object that matches these criteria, in order of preference: * (a) Level 1 object with the Highlight option set to "Y" * (b) Level 1 object with the Highlight option missing or set to other than "Y" or "N" * (c) Level 2 or higher object with the Highlight option set to "Y" * (d) Level 2 or higher object with the Highlight option missing or set to other than "Y" or "N" * Criterion (d) will be present in the code but will be commented out for now. * * @param string $pid the individual, source, or family id * @param string $indirec the gedcom record to look in * @return array an object array with indexes "thumb" and "file" for thumbnail and filename */ function find_highlighted_object($pid, $ged_id, $indirec) { global $MEDIA_DIRECTORY, $MEDIA_DIRECTORY_LEVELS, $WT_IMAGE_DIR, $WT_IMAGES, $MEDIA_EXTERNAL, $TBLPREFIX; if (!showFactDetails("OBJE", $pid)) { return false; } $media = array(); $objectA = array(); $objectB = array(); $objectC = array(); $objectD = array(); //-- handle finding the media of remote objects $ct = preg_match("/(.*):(.*)/", $pid, $match); if ($ct>0) { require_once WT_ROOT.'includes/classes/class_serviceclient.php'; $client = ServiceClient::getInstance($match[1]); if (!is_null($client)) { $mt = preg_match_all('/\n\d OBJE @('.WT_REGEX_XREF.')@/', $indirec, $matches, PREG_SET_ORDER); for ($i=0; $i<$mt; $i++) { $mediaObj = Media::getInstance($matches[$i][1]); $mrec = $mediaObj->getGedcomRecord(); if (!empty($mrec)) { $file = get_gedcom_value("FILE", 1, $mrec); $row = array($matches[$i][1], $file, $mrec, $matches[$i][0]); $media[] = $row; } } } } //-- find all of the media items for a person $media= WT_DB::prepare("SELECT m_media, m_file, m_gedrec, mm_gedrec FROM {$TBLPREFIX}media, {$TBLPREFIX}media_mapping WHERE m_media=mm_media AND m_gedfile=mm_gedfile AND m_gedfile=? AND mm_gid=? ORDER BY mm_order") ->execute(array($ged_id, $pid)) ->fetchAll(PDO::FETCH_NUM); foreach ($media as $i=>$row) { if (displayDetailsById($row[0], 'OBJE') && !FactViewRestricted($row[0], $row[2])) { $level=0; $ct = preg_match("/(\d+) OBJE/", $row[3], $match); if ($ct>0) { $level = $match[1]; } if (strstr($row[3], "_PRIM ")) { $thum = get_gedcom_value('_THUM', $level+1, $row[3]); $prim = get_gedcom_value('_PRIM', $level+1, $row[3]); } else { $thum = get_gedcom_value('_THUM', 1, $row[2]); $prim = get_gedcom_value('_PRIM', 1, $row[2]); } if ($prim=='N') continue; // Skip _PRIM N objects $file = check_media_depth($row[1]); $thumb = thumbnail_file($row[1], true, false, $pid); if ($level == 1) { if ($prim == 'Y') { if (empty($objectA)) { $objectA['file'] = $file; $objectA['thumb'] = $thumb; $objectA['_THUM'] = $thum; // This overrides GEDCOM's "Use main image as thumbnail" option $objectA['level'] = $level; $objectA['mid'] = $row[0]; } } else { if (empty($objectB)) { $objectB['file'] = $file; $objectB['thumb'] = $thumb; $objectB['_THUM'] = $thum; // This overrides GEDCOM's "Use main image as thumbnail" option $objectB['level'] = $level; $objectB['mid'] = $row[0]; } } } else { if ($prim == 'Y') { if (empty($objectC)) { $objectC['file'] = $file; $objectC['thumb'] = $thumb; $objectC['_THUM'] = $thum; // This overrides GEDCOM's "Use main image as thumbnail" option $objectC['level'] = $level; $objectC['mid'] = $row[0]; } } else { if (empty($objectD)) { $objectD['file'] = $file; $objectD['thumb'] = $thumb; $objectD['_THUM'] = $thum; // This overrides GEDCOM's "Use main image as thumbnail" option $objectD['level'] = $level; $objectD['mid'] = $row[0]; } } } } } if (!empty($objectA)) return $objectA; if (!empty($objectB)) return $objectB; if (!empty($objectC)) return $objectC; // if (!empty($objectD)) return $objectD; return array(); } /** * Determine whether the main image or a thumbnail should be sent to the browser */ function thumb_or_main($object) { global $USE_THUMBS_MAIN; if ($object['_THUM']=='Y' || !$USE_THUMBS_MAIN) $file = 'file'; else $file = 'thumb'; // Here we should check whether the selected file actually exists return($object[$file]); } /** * get the full file path * * get the file path from a multimedia gedcom record * @param string $mediarec a OBJE subrecord * @return the fullpath from the FILE record */ function extract_fullpath($mediarec) { preg_match("/(\d) _*FILE (.*)/", $mediarec, $amatch); if (empty($amatch[2])) { return ""; } $level = trim($amatch[1]); $fullpath = trim($amatch[2]); $filerec = get_sub_record($level, $amatch[0], $mediarec); $fullpath .= get_cont($level+1, $filerec); return $fullpath; } /** * get the relative filename for a media item * * gets the relative file path from the full media path for a media item. checks the * $MEDIA_DIRECTORY_LEVELS to make sure the directory structure is maintained. * @param string $fullpath the full path from the media record * @return string a relative path that can be appended to the $MEDIA_DIRECTORY to reference the item */ function extract_filename($fullpath) { global $MEDIA_DIRECTORY_LEVELS, $MEDIA_DIRECTORY; $filename=""; $regexp = "'[/\\\]'"; $srch = "/".addcslashes($MEDIA_DIRECTORY, '/.')."/"; $repl = ""; if (!isFileExternal($fullpath)) { $nomedia = stripcslashes(preg_replace($srch, $repl, $fullpath)); } else { $nomedia = $fullpath; } $ct = preg_match($regexp, $nomedia, $match); if ($ct>0) { $subelements = preg_split($regexp, $nomedia); $subelements = array_reverse($subelements); $max = $MEDIA_DIRECTORY_LEVELS; if ($max>=count($subelements)) { $max=count($subelements)-1; } for ($s=$max; $s>=0; $s--) { if ($s!=$max) { $filename = $filename."/".$subelements[$s]; } else { $filename = $subelements[$s]; } } } else { $filename = $nomedia; } return $filename; } // ************************************************* START OF SORTING FUNCTIONS ********************************* // /** * Function to sort GEDCOM fact tags based on their tanslations */ function factsort($a, $b) { return utf8_strcasecmp(i18n::translate($a), i18n::translate($b)); } /** * Function to sort place names array */ function placesort($a, $b) { return utf8_strcasecmp($a['place'], $b['place']); } //////////////////////////////////////////////////////////////////////////////// // Sort a list events for the today/upcoming blocks //////////////////////////////////////////////////////////////////////////////// function event_sort($a, $b) { if ($a['jd']==$b['jd']) { if ($a['anniv']==$b['anniv']) { return utf8_strcasecmp($a['fact'], $b['fact']); } else { return utf8_strcasecmp($a['anniv'], $b['anniv']); } } else { return $a['jd']-$b['jd']; } } function event_sort_name($a, $b) { if ($a['jd']==$b['jd']) { return GedcomRecord::compare($a['record'], $b['record']); } else { return $a['jd']-$b['jd']; } } /** * sort an array of media items * */ function mediasort($a, $b) { $aKey = ""; if (!empty($a["TITL"])) { $aKey = $a["TITL"]; } else { if (!empty($a["titl"])) { $aKey = $a["titl"]; } else { if (!empty($a["NAME"])) { $aKey = $a["NAME"]; } else { if (!empty($a["name"])) { $aKey = $a["name"]; } else { if (!empty($a["FILE"])) { $aKey = basename($a["FILE"]); } else { if (!empty($a["file"])) { $aKey = basename($a["file"]); } } } } } } $bKey = ""; if (!empty($b["TITL"])) { $bKey = $b["TITL"]; } else { if (!empty($b["titl"])) { $bKey = $b["titl"]; } else { if (!empty($b["NAME"])) { $bKey = $b["NAME"]; } else { if (!empty($b["name"])) { $bKey = $b["name"]; } else { if (!empty($b["FILE"])) { $bKey = basename($b["FILE"]); } else { if (!empty($b["file"])) { $bKey = basename($b["file"]); } } } } } } return utf8_strcasecmp($aKey, $bKey, true); // Case-insensitive compare } /** * sort an array according to the file name * */ function filesort($a, $b) { $aKey = ""; if (!empty($a["FILE"])) { $aKey = basename($a["FILE"]); } else if (!empty($a["file"])) { $aKey = basename($a["file"]); } $bKey = ""; if (!empty($b["FILE"])) { $bKey = basename($b["FILE"]); } else if (!empty($b["file"])) { $bKey = basename($b["file"]); } return utf8_strcasecmp($aKey, $bKey, true); // Case-insensitive compare } // Helper function to sort facts. function compare_facts_date($arec, $brec) { if (is_array($arec)) $arec = $arec[1]; if (is_array($brec)) $brec = $brec[1]; // If either fact is undated, the facts sort equally. if (!preg_match("/2 _?DATE (.*)/", $arec, $amatch) || !preg_match("/2 _?DATE (.*)/", $brec, $bmatch)) { if (preg_match('/2 _SORT (\d+)/', $arec, $match1) && preg_match('/2 _SORT (\d+)/', $brec, $match2)) { return $match1[1]-$match2[1]; } return 0; } $adate = new GedcomDate($amatch[1]); $bdate = new GedcomDate($bmatch[1]); // If either date can't be parsed, don't sort. if (!$adate->isOK() || !$bdate->isOK()) { if (preg_match('/2 _SORT (\d+)/', $arec, $match1) && preg_match('/2 _SORT (\d+)/', $brec, $match2)) { return $match1[1]-$match2[1]; } return 0; } // Remember that dates can be ranges and overlapping ranges sort equally. $amin=$adate->MinJD(); $bmin=$bdate->MinJD(); $amax=$adate->MaxJD(); $bmax=$bdate->MaxJD(); // BEF/AFT XXX sort as the day before/after XXX if ($adate->qual1=='BEF') { $amin=$amin-1; $amax=$amin; } elseif ($adate->qual1=='AFT') { $amax=$amax+1; $amin=$amax; } if ($bdate->qual1=='BEF') { $bmin=$bmin-1; $bmax=$bmin; } elseif ($bdate->qual1=='AFT') { $bmax=$bmax+1; $bmin=$bmax; } if ($amax<$bmin) { return -1; } else { if ($amin>$bmax) { return 1; } else { //-- ranged date... take the type of fact sorting into account $factWeight = 0; if (preg_match('/2 _SORT (\d+)/', $arec, $match1) && preg_match('/2 _SORT (\d+)/', $brec, $match2)) { $factWeight = $match1[1]-$match2[1]; } //-- fact is prefered to come before, so compare using the minimum ranges if ($factWeight < 0 && $amin!=$bmin) { return ($amin-$bmin); } else { if ($factWeight > 0 && $bmax!=$amax) { //-- fact is prefered to come after, so compare using the max of the ranges return ($bmax-$amax); } else { //-- facts are the same or the ranges don't give enough info, so use the average of the range $aavg = ($amin+$amax)/2; $bavg = ($bmin+$bmax)/2; if ($aavg<$bavg) { return -1; } else { if ($aavg>$bavg) { return 1; } else { return $factWeight; } } } } return 0; } } } /** * A multi-key sort * 1. First divide the facts into two arrays one set with dates and one set without dates * 2. Sort each of the two new arrays, the date using the compare date function, the non-dated * using the compare type function * 3. Then merge the arrays back into the original array using the compare type function * * @param unknown_type $arr */ function sort_facts(&$arr) { $dated = array(); $nondated = array(); //-- split the array into dated and non-dated arrays $order = 0; foreach($arr as $event) { $event->sortOrder = $order; $order++; if ($event->getValue("DATE")==NULL || !$event->getDate()->isOk()) $nondated[] = $event; else $dated[] = $event; } //-- sort each type of array usort($dated, array("Event", "CompareDate")); usort($nondated, array("Event", "CompareType")); //-- merge the arrays back together comparing by Facts $dc = count($dated); $nc = count($nondated); $i = 0; $j = 0; $k = 0; // while there is anything in the dated array continue merging while($i<$dc) { // compare each fact by type to merge them in order if ($j<$nc && Event::CompareType($dated[$i], $nondated[$j])>0) { $arr[$k] = $nondated[$j]; $j++; } else { $arr[$k] = $dated[$i]; $i++; } $k++; } // get anything that might be left in the nondated array while($j<$nc) { $arr[$k] = $nondated[$j]; $j++; $k++; } } function gedcomsort($a, $b) { return utf8_strcasecmp($a["title"], $b["title"]); } // ************************************************* START OF MISCELLANIOUS FUNCTIONS ********************************* // /** * Get relationship between two individuals in the gedcom * * function to calculate the relationship between two people it uses hueristics based on the * individuals birthdate to try and calculate the shortest path between the two individuals * it uses a node cache to help speed up calculations when using relationship privacy * this cache is indexed using the string "$pid1-$pid2" * @param string $pid1 the ID of the first person to compute the relationship from * @param string $pid2 the ID of the second person to compute the relatiohip to * @param bool $followspouse whether to add spouses to the path * @param int $maxlenght the maximim length of path * @param bool $ignore_cache enable or disable the relationship cache * @param int $path_to_find which path in the relationship to find, 0 is the shortest path, 1 is the next shortest path, etc */ function get_relationship($pid1, $pid2, $followspouse=true, $maxlength=0, $ignore_cache=false, $path_to_find=0) { global $start_time, $NODE_CACHE, $NODE_CACHE_LENGTH, $USE_RELATIONSHIP_PRIVACY; $time_limit=get_site_setting('MAX_EXECUTION_TIME'); $indirec = find_gedcom_record($pid2, WT_GED_ID, WT_USER_CAN_EDIT); //-- check the cache if ($USE_RELATIONSHIP_PRIVACY && !$ignore_cache) { if (isset($NODE_CACHE["$pid1-$pid2"])) { if ($NODE_CACHE["$pid1-$pid2"]=="NOT FOUND") return false; if (($maxlength==0)||(count($NODE_CACHE["$pid1-$pid2"]["path"])-1<=$maxlength)) return $NODE_CACHE["$pid1-$pid2"]; else return false; } //-- check the cache for person 2's children $famids = array(); $ct = preg_match_all("/1 FAMS @(.*)@/", $indirec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { $famids[$i]=$match[$i][1]; } foreach ($famids as $indexval => $fam) { $famrec = find_gedcom_record($fam, WT_GED_ID, WT_USER_CAN_EDIT); $ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { $child = $match[$i][1]; if (!empty($child)) { if (isset($NODE_CACHE["$pid1-$child"])) { if (($maxlength==0)||(count($NODE_CACHE["$pid1-$child"]["path"])+1<=$maxlength)) { $node1 = $NODE_CACHE["$pid1-$child"]; if ($node1!="NOT FOUND") { $node1["path"][] = $pid2; $node1["pid"] = $pid2; if (strpos($indirec, "1 SEX F")!==false) $node1["relations"][] = "mother"; else $node1["relations"][] = "father"; } $NODE_CACHE["$pid1-$pid2"] = $node1; if ($node1=="NOT FOUND") return false; return $node1; } else return false; } } } } if ((!empty($NODE_CACHE_LENGTH))&&($maxlength>0)) { if ($NODE_CACHE_LENGTH>=$maxlength) return false; } } //-- end cache checking //-- get the birth year of p2 for calculating heuristics $birthrec = get_sub_record(1, "1 BIRT", $indirec); $byear2 = -1; if ($birthrec!==false) { $dct = preg_match("/2 DATE .*(\d\d\d\d)/", $birthrec, $match); if ($dct>0) $byear2 = $match[1]; } if ($byear2==-1) { $numfams = preg_match_all("/1 FAMS @(.*)@/", $indirec, $fmatch, PREG_SET_ORDER); for ($j=0; $j<$numfams; $j++) { // Get the family record $famrec = find_gedcom_record($fmatch[$j][1], WT_GED_ID, WT_USER_CAN_EDIT); // Get the set of children $ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $cmatch, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { // Get each child's record $childrec = find_gedcom_record($cmatch[$i][1], WT_GED_ID, WT_USER_CAN_EDIT); $birthrec = get_sub_record(1, "1 BIRT", $childrec); if ($birthrec!==false) { $dct = preg_match("/2 DATE .*(\d\d\d\d)/", $birthrec, $bmatch); if ($dct>0) $byear2 = $bmatch[1]-25; if ($byear2>2100) $byear2-=3760; // Crude conversion from jewish to gregorian } } } } //-- end of approximating birth year //-- current path nodes $p1nodes = array(); //-- ids visited $visited = array(); //-- set up first node for person1 $node1 = array(); $node1["path"] = array(); $node1["path"][] = $pid1; $node1["length"] = 0; $node1["pid"] = $pid1; $node1["relations"] = array(); $node1["relations"][] = "self"; $p1nodes[] = $node1; $visited[$pid1] = true; $found = false; $count=0; while (!$found) { //-- the following 2 lines ensure that the user can abort a long relationship calculation //-- refer to http://www.php.net/manual/en/features.connection-handling.php for more //-- information about why these lines are included if (headers_sent()) { print " "; if ($count%100 == 0) flush(); } $count++; $end_time = microtime(true); $exectime = $end_time - $start_time; if (($time_limit>1)&&($exectime > $time_limit-1)) { echo "", i18n::translate('The script timed out before a relationship could be found.'), "\n"; return false; } if (count($p1nodes)==0) { if ($maxlength!=0) { if (!isset($NODE_CACHE_LENGTH)) { $NODE_CACHE_LENGTH = $maxlength; } elseif ($NODE_CACHE_LENGTH<$maxlength) { $NODE_CACHE_LENGTH = $maxlength; } } if (headers_sent()) { //print "\n\n"; } $NODE_CACHE["$pid1-$pid2"] = "NOT FOUND"; return false; } //-- search the node list for the shortest path length $shortest = -1; foreach ($p1nodes as $index=>$node) { if ($shortest == -1) { $shortest = $index; } else { $node1 = $p1nodes[$shortest]; if ($node1["length"] > $node["length"]) { $shortest = $index; } } } if ($shortest==-1) return false; $node = $p1nodes[$shortest]; if (($maxlength==0)||(count($node["path"])<=$maxlength)) { if ($node["pid"]==$pid2) { } else { //-- hueristic values $fatherh = 1; $motherh = 1; $siblingh = 2; $spouseh = 2; $childh = 3; //-- generate heuristic values based of the birthdates of the current node and p2 $indirec = find_gedcom_record($node["pid"], WT_GED_ID, WT_USER_CAN_EDIT); $byear1 = -1; $birthrec = get_sub_record(1, "1 BIRT", $indirec); if ($birthrec!==false) { $dct = preg_match("/2 DATE .*(\d\d\d\d)/", $birthrec, $match); if ($dct>0) $byear1 = $match[1]; if ($byear1>2100) $byear1-=3760; // Crude conversion from jewish to gregorian } if (($byear1!=-1)&&($byear2!=-1)) { $yeardiff = $byear1-$byear2; if ($yeardiff < -140) { $fatherh = 20; $motherh = 20; $siblingh = 15; $spouseh = 15; $childh = 1; } else if ($yeardiff < -100) { $fatherh = 15; $motherh = 15; $siblingh = 10; $spouseh = 10; $childh = 1; } else if ($yeardiff < -60) { $fatherh = 10; $motherh = 10; $siblingh = 5; $spouseh = 5; $childh = 1; } else if ($yeardiff < -20) { $fatherh = 5; $motherh = 5; $siblingh = 3; $spouseh = 3; $childh = 1; } else if ($yeardiff<20) { $fatherh = 3; $motherh = 3; $siblingh = 1; $spouseh = 1; $childh = 5; } else if ($yeardiff<60) { $fatherh = 1; $motherh = 1; $siblingh = 5; $spouseh = 2; $childh = 10; } else if ($yeardiff<100) { $fatherh = 1; $motherh = 1; $siblingh = 10; $spouseh = 3; $childh = 15; } else { $fatherh = 1; $motherh = 1; $siblingh = 15; $spouseh = 4; $childh = 20; } } //-- check all parents and siblings of this node $famids = array(); $ct = preg_match_all("/1 FAMC @(.*)@/", $indirec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { if (!isset($visited[$match[$i][1]])) $famids[$i]=$match[$i][1]; } foreach ($famids as $indexval => $fam) { $visited[$fam] = true; $famrec = find_gedcom_record($fam, WT_GED_ID, WT_USER_CAN_EDIT); $parents = find_parents_in_record($famrec); if ((!empty($parents["HUSB"]))&&(!isset($visited[$parents["HUSB"]]))) { $node1 = $node; $node1["length"]+=$fatherh; $node1["path"][] = $parents["HUSB"]; $node1["pid"] = $parents["HUSB"]; $node1["relations"][] = "parent"; $p1nodes[] = $node1; if ($node1["pid"]==$pid2) { if ($path_to_find>0) $path_to_find--; else { $found=true; $resnode = $node1; } } else $visited[$parents["HUSB"]] = true; if ($USE_RELATIONSHIP_PRIVACY) { $NODE_CACHE["$pid1-".$node1["pid"]] = $node1; } } if ((!empty($parents["WIFE"]))&&(!isset($visited[$parents["WIFE"]]))) { $node1 = $node; $node1["length"]+=$motherh; $node1["path"][] = $parents["WIFE"]; $node1["pid"] = $parents["WIFE"]; $node1["relations"][] = "parent"; $p1nodes[] = $node1; if ($node1["pid"]==$pid2) { if ($path_to_find>0) $path_to_find--; else { $found=true; $resnode = $node1; } } else $visited[$parents["WIFE"]] = true; if ($USE_RELATIONSHIP_PRIVACY) { $NODE_CACHE["$pid1-".$node1["pid"]] = $node1; } } $ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { $child = $match[$i][1]; if ((!empty($child))&&(!isset($visited[$child]))) { $node1 = $node; $node1["length"]+=$siblingh; $node1["path"][] = $child; $node1["pid"] = $child; $node1["relations"][] = "sibling"; $p1nodes[] = $node1; if ($node1["pid"]==$pid2) { if ($path_to_find>0) $path_to_find--; else { $found=true; $resnode = $node1; } } else $visited[$child] = true; if ($USE_RELATIONSHIP_PRIVACY) { $NODE_CACHE["$pid1-".$node1["pid"]] = $node1; } } } } //-- check all spouses and children of this node $famids = array(); $ct = preg_match_all("/1 FAMS @(.*)@/", $indirec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { $famids[$i]=$match[$i][1]; } foreach ($famids as $indexval => $fam) { $visited[$fam] = true; $famrec = find_gedcom_record($fam, WT_GED_ID, WT_USER_CAN_EDIT); if ($followspouse) { $parents = find_parents_in_record($famrec); if ((!empty($parents["HUSB"]))&&((!in_arrayr($parents["HUSB"], $node1))||(!isset($visited[$parents["HUSB"]])))) { $node1 = $node; $node1["length"]+=$spouseh; $node1["path"][] = $parents["HUSB"]; $node1["pid"] = $parents["HUSB"]; $node1["relations"][] = "spouse"; $p1nodes[] = $node1; if ($node1["pid"]==$pid2) { if ($path_to_find>0) $path_to_find--; else { $found=true; $resnode = $node1; } } else $visited[$parents["HUSB"]] = true; if ($USE_RELATIONSHIP_PRIVACY) { $NODE_CACHE["$pid1-".$node1["pid"]] = $node1; } } if ((!empty($parents["WIFE"]))&&((!in_arrayr($parents["WIFE"], $node1))||(!isset($visited[$parents["WIFE"]])))) { $node1 = $node; $node1["length"]+=$spouseh; $node1["path"][] = $parents["WIFE"]; $node1["pid"] = $parents["WIFE"]; $node1["relations"][] = "spouse"; $p1nodes[] = $node1; if ($node1["pid"]==$pid2) { if ($path_to_find>0) $path_to_find--; else { $found=true; $resnode = $node1; } } else $visited[$parents["WIFE"]] = true; if ($USE_RELATIONSHIP_PRIVACY) { $NODE_CACHE["$pid1-".$node1["pid"]] = $node1; } } } $ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $match, PREG_SET_ORDER); for ($i=0; $i<$ct; $i++) { $child = $match[$i][1]; if ((!empty($child))&&(!isset($visited[$child]))) { $node1 = $node; $node1["length"]+=$childh; $node1["path"][] = $child; $node1["pid"] = $child; $node1["relations"][] = "child"; $p1nodes[] = $node1; if ($node1["pid"]==$pid2) { if ($path_to_find>0) $path_to_find--; else { $found=true; $resnode = $node1; } } else { $visited[$child] = true; } if ($USE_RELATIONSHIP_PRIVACY) { $NODE_CACHE["$pid1-".$node1["pid"]] = $node1; } } } } } } unset($p1nodes[$shortest]); } //-- end while loop if (headers_sent()) { //echo "\n\n"; } // Convert "generic" relationships into sex-specific ones. foreach ($resnode['path'] as $n=>$pid) { switch ($resnode['relations'][$n]) { case 'parent': switch (Person::getInstance($pid)->getSex()) { case 'M': $resnode['relations'][$n]='father'; break; case 'F': $resnode['relations'][$n]='mother'; break; } break; case 'child': switch (Person::getInstance($pid)->getSex()) { case 'M': $resnode['relations'][$n]='son'; break; case 'F': $resnode['relations'][$n]='daughter'; break; } break; case 'spouse': switch (Person::getInstance($pid)->getSex()) { case 'M': $resnode['relations'][$n]='husband'; break; case 'F': $resnode['relations'][$n]='wife'; break; } break; case 'sibling': switch (Person::getInstance($pid)->getSex()) { case 'M': $resnode['relations'][$n]='brother'; break; case 'F': $resnode['relations'][$n]='sister'; break; } break; } } return $resnode; } // Convert the result of get_relationship() into a relationship name. function get_relationship_name($nodes) { if (!is_array($nodes)) { return ''; } $pid1=$nodes['path'][0]; $pid2=$nodes['path'][count($nodes['path'])-1]; $path=array_slice($nodes['relations'], 1); // Look for paths with *specific* names first. // Note that every combination must be listed separately, as the same english // name can be used for many different relationships. e.g. // brother's wife & husband's sister = sister-in-law. // // $path is an array of the 12 possible gedcom family relationships: // mother/father/parent // brother/sister/sibling // husband/wife/spouse // son/daughter/child // // This is always the shortest path, so "father, daughter" is "half-sister", not "sister". // // This is very repetitive in english, but necessary in order to handle the // complexities of other languages. // // TODO: handle unmarried partners, so need male-partner, female-partner, unknown-partner // Make each relationship parts the same length, for simpler matching. $combined_path=''; foreach ($path as $rel) { $combined_path.=substr($rel, 0, 3); } return get_relationship_name_from_path($combined_path, $pid1, $pid2); } function get_relationship_name_from_path($path, $pid1, $pid2) { if (!preg_match('/^(mot|fat|par|hus|wif|spo|son|dau|chi|bro|sis|sib)*$/', $path)) { // TODO: Update all the "3 RELA " values in class_person return ''.$path.''; } switch ($path) { case '': return i18n::translate('self'); // Level One relationships case 'mot': return i18n::translate('mother'); case 'fat': return i18n::translate('father'); case 'par': return i18n::translate('parent'); case 'hus': return i18n::translate('husband'); case 'wif': return i18n::translate('wife'); case 'spo': return i18n::translate('spouse'); case 'son': return i18n::translate('son'); case 'dau': return i18n::translate('daughter'); case 'chi': return i18n::translate('child'); case 'bro': $dob1=Person::GetInstance($pid1)->getBirthDate(); $dob2=Person::GetInstance($pid2)->getBirthDate(); if ($dob1->isOK() && $dob2->isOK()) { if (abs($dob1->JD()-$dob2->JD())<2) { return i18n::translate('twin brother'); } else if ($dob1->JD()<$dob2->JD()) { return i18n::translate('younger brother'); } else { return i18n::translate('elder brother'); } } return i18n::translate('brother'); case 'sis': $dob1=Person::GetInstance($pid1)->getBirthDate(); $dob2=Person::GetInstance($pid2)->getBirthDate(); if ($dob1->isOK() && $dob2->isOK()) { if (abs($dob1->JD()-$dob2->JD())<2) { return i18n::translate('twin sister'); } else if ($dob1->JD()<$dob2->JD()) { return i18n::translate('younger sister'); } else { return i18n::translate('elder sister'); } } return i18n::translate('sister'); case 'sib': $dob1=Person::GetInstance($pid1)->getBirthDate(); $dob2=Person::GetInstance($pid2)->getBirthDate(); if ($dob1->isOK() && $dob2->isOK()) { if (abs($dob1->JD()-$dob2->JD())<2) { return i18n::translate('twin sibling'); } else if ($dob1->JD()<$dob2->JD()) { return i18n::translate('younger sibling'); } else { return i18n::translate('elder sibling'); } } return i18n::translate('sibling'); // Level Two relationships case 'motmot': return i18n::translate_c('mother\'s mother', 'grandmother'); case 'motfat': return i18n::translate_c('mother\'s father', 'grandfather'); case 'motpar': return i18n::translate_c('mother\'s parent', 'grandparent'); case 'fatmot': return i18n::translate_c('father\'s mother', 'grandmother'); case 'fatfat': return i18n::translate_c('father\'s father', 'grandfather'); case 'fatpar': return i18n::translate_c('father\'s parent', 'grandparent'); case 'parmot': return i18n::translate_c('parent\'s mother', 'grandmother'); case 'parfat': return i18n::translate_c('parent\'s father', 'grandfather'); case 'parpar': return i18n::translate_c('parent\'s parent', 'grandparent'); case 'daudau': return i18n::translate_c('daughter\'s daughter', 'granddaughter'); case 'dauson': return i18n::translate_c('daughter\'s son', 'grandson'); case 'dauchi': return i18n::translate_c('daughter\'s child', 'grandchild'); case 'sondau': return i18n::translate_c('son\'s daughter', 'granddaughter'); case 'sonson': return i18n::translate_c('son\'s son', 'grandson'); case 'sonchi': return i18n::translate_c('son\'s child', 'grandchild'); case 'chidau': return i18n::translate_c('child\'s daughter', 'granddaughter'); case 'chison': return i18n::translate_c('child\'s son', 'grandson'); case 'chichi': return i18n::translate_c('child\'s child', 'grandchild'); case 'mothus': return i18n::translate_c('mother\'s husband', 'step-father'); case 'mothus': return i18n::translate_c('mother\'s husband', 'step-father'); case 'fatwif': return i18n::translate_c('father\'s wife', 'step-mother'); case 'fatwif': return i18n::translate_c('father\'s wife', 'step-mother'); case 'parspo': return i18n::translate_c('parent\'s spouse', 'step-parent'); case 'parspo': return i18n::translate_c('parent\'s spouse', 'step-parent'); case 'motson': return i18n::translate_c('mother\'s son', 'half-brother'); case 'motdau': return i18n::translate_c('mother\'s daughter', 'half-sister'); case 'motchi': return i18n::translate_c('mother\'s child', 'half-sibling'); case 'fatson': return i18n::translate_c('father\'s son', 'half-brother'); case 'fatdau': return i18n::translate_c('father\'s daughter', 'half-sister'); case 'fatchi': return i18n::translate_c('father\'s child', 'half-sibling'); case 'parson': return i18n::translate_c('parent\'s son', 'half-brother'); case 'pardau': return i18n::translate_c('parent\'s daughter', 'half-sister'); case 'parchi': return i18n::translate_c('parent\'s child', 'half-sibling'); case 'motsis': return i18n::translate_c('mother\'s sister', 'aunt'); case 'motbro': return i18n::translate_c('mother\'s brother', 'uncle'); case 'motsib': return i18n::translate_c('mother\'s sibling', 'aunt/uncle'); case 'fatsis': return i18n::translate_c('father\'s sister', 'aunt'); case 'fatbro': return i18n::translate_c('father\'s brother', 'uncle'); case 'fatsib': return i18n::translate_c('father\'s sibling', 'aunt/uncle'); case 'parsis': return i18n::translate_c('parent\'s sister', 'aunt'); case 'parbro': return i18n::translate_c('parent\'s brother', 'uncle'); case 'parsib': return i18n::translate_c('parent\'s sibling', 'aunt/uncle'); case 'broson': return i18n::translate_c('brother\'s son', 'nephew'); case 'brodau': return i18n::translate_c('brother\'s daughter', 'niece'); case 'brochi': return i18n::translate_c('brother\'s child', 'nephew/neice'); case 'sisson': return i18n::translate_c('sister\'s son', 'nephew'); case 'sisdau': return i18n::translate_c('sister\'s daughter', 'niece'); case 'sischi': return i18n::translate_c('sister\'s child', 'nephew/neice'); case 'sibson': return i18n::translate_c('sibling\'s son', 'nephew'); case 'sibdau': return i18n::translate_c('sibling\'s daughter', 'niece'); case 'sibchi': return i18n::translate_c('sibling\'s child', 'nephew/neice'); case 'wifsis': return i18n::translate_c('wife\'s sister', 'sister-in-law'); case 'hussis': return i18n::translate_c('husband\'s sister', 'sister-in-law'); case 'sposis': return i18n::translate_c('spouses\'s sister', 'sister-in-law'); case 'wifbro': return i18n::translate_c('wife\'s brother', 'brother-in-law'); case 'husbro': return i18n::translate_c('husband\'s brother', 'brother-in-law'); case 'spobro': return i18n::translate_c('spouses\'s brother', 'brother-in-law'); case 'browif': return i18n::translate_c('brother\'s wife', 'sister-in-law'); case 'sishus': return i18n::translate_c('sister\'s husband', 'brother-in-law'); case 'husmot': return i18n::translate_c('husband\'s mother', 'mother-in-law'); case 'wifmot': return i18n::translate_c('wife\'s mother', 'mother-in-law'); case 'spomot': return i18n::translate_c('spouses\'s mother', 'mother-in-law'); case 'husfat': return i18n::translate_c('husband\'s father', 'father-in-law'); case 'wiffat': return i18n::translate_c('wife\'s father', 'father-in-law'); case 'spofat': return i18n::translate_c('spouses\'s father', 'father-in-law'); case 'sonwif': return i18n::translate_c('son\'s wife', 'daughter-in-law'); case 'dauhus': return i18n::translate_c('daughter\'s husband', 'son-in-law'); case 'husson': return i18n::translate_c('husband\'s son', 'step-son'); case 'wifson': return i18n::translate_c('wife\'s son', 'step-son'); case 'sposon': return i18n::translate_c('spouses\'s son', 'step-son'); case 'husdau': return i18n::translate_c('husband\'s daughter', 'step-daughter'); case 'wifdau': return i18n::translate_c('wife\'s daughter', 'step-daughter'); case 'spodau': return i18n::translate_c('spouses\'s daughter', 'step-daughter'); case 'huschi': return i18n::translate_c('husband\'s child', 'step-child'); case 'wifchi': return i18n::translate_c('wife\'s child', 'step-child'); case 'spochi': return i18n::translate_c('spouses\'s child', 'step-child'); // Level Three relationships case 'motmotmot': return i18n::translate_c('mother\'s mother\'s mother', 'great-grandmother'); case 'motmotfat': return i18n::translate_c('mother\'s mother\'s father', 'great-grandfather'); case 'motmotpar': return i18n::translate_c('mother\'s mother\'s parent', 'great-grandparent'); case 'motfatmot': return i18n::translate_c('mother\'s father\'s mother', 'great-grandmother'); case 'motfatfat': return i18n::translate_c('mother\'s father\'s father', 'great-grandfather'); case 'motfatpar': return i18n::translate_c('mother\'s father\'s parent', 'great-grandparent'); case 'motparmot': return i18n::translate_c('mother\'s parent\'s mother', 'great-grandmother'); case 'motparfat': return i18n::translate_c('mother\'s parent\'s father', 'great-grandfather'); case 'motparpar': return i18n::translate_c('mother\'s parent\'s parent', 'great-grandparent'); case 'fatmotmot': return i18n::translate_c('father\'s mother\'s mother', 'great-grandmother'); case 'fatmotfat': return i18n::translate_c('father\'s mother\'s father', 'great-grandfather'); case 'fatmotpar': return i18n::translate_c('father\'s mother\'s parent', 'great-grandparent'); case 'fatfatmot': return i18n::translate_c('father\'s father\'s mother', 'great-grandmother'); case 'fatfatfat': return i18n::translate_c('father\'s father\'s father', 'great-grandfather'); case 'fatfatpar': return i18n::translate_c('father\'s father\'s parent', 'great-grandparent'); case 'fatparmot': return i18n::translate_c('father\'s parent\'s mother', 'great-grandmother'); case 'fatparfat': return i18n::translate_c('father\'s parent\'s father', 'great-grandfather'); case 'fatparpar': return i18n::translate_c('father\'s parent\'s parent', 'great-grandparent'); case 'parmotmot': return i18n::translate_c('parent\'s mother\'s mother', 'great-grandmother'); case 'parmotfat': return i18n::translate_c('parent\'s mother\'s father', 'great-grandfather'); case 'parmotpar': return i18n::translate_c('parent\'s mother\'s parent', 'great-grandparent'); case 'parfatmot': return i18n::translate_c('parent\'s father\'s mother', 'great-grandmother'); case 'parfatfat': return i18n::translate_c('parent\'s father\'s father', 'great-grandfather'); case 'parfatpar': return i18n::translate_c('parent\'s father\'s parent', 'great-grandparent'); case 'parparmot': return i18n::translate_c('parent\'s parent\'s mother', 'great-grandmother'); case 'parparfat': return i18n::translate_c('parent\'s parent\'s father', 'great-grandfather'); case 'parparpar': return i18n::translate_c('parent\'s parent\'s parent', 'great-grandparent'); case 'motmotsis': return i18n::translate_c('mother\'s mother\'s sister', 'great-aunt'); case 'motmotbro': return i18n::translate_c('mother\'s mother\'s brother', 'great-uncle'); case 'motmotsib': return i18n::translate_c('mother\'s mother\'s sibling', 'great-aunt/uncle'); case 'motfatsis': return i18n::translate_c('mother\'s father\'s sister', 'great-aunt'); case 'motfatbro': return i18n::translate_c('mother\'s father\'s brother', 'great-uncle'); case 'motfatsib': return i18n::translate_c('mother\'s father\'s sibling', 'great-aunt/uncle'); case 'motparsis': return i18n::translate_c('mother\'s parent\'s sister', 'great-aunt'); case 'motparbro': return i18n::translate_c('mother\'s parent\'s brother', 'great-uncle'); case 'motparsib': return i18n::translate_c('mother\'s parent\'s sibling', 'great-aunt/uncle'); case 'fatmotsis': return i18n::translate_c('father\'s mother\'s sister', 'great-aunt'); case 'fatmotbro': return i18n::translate_c('father\'s mother\'s brother', 'great-uncle'); case 'fatmotsib': return i18n::translate_c('father\'s mother\'s sibling', 'great-aunt/uncle'); case 'fatfatsis': return i18n::translate_c('father\'s father\'s sister', 'great-aunt'); case 'fatfatbro': return i18n::translate_c('father\'s father\'s brother', 'great-uncle'); case 'fatfatsib': return i18n::translate_c('father\'s father\'s sibling', 'great-aunt/uncle'); case 'fatparsis': return i18n::translate_c('father\'s parent\'s sister', 'great-aunt'); case 'fatparbro': return i18n::translate_c('father\'s parent\'s brother', 'great-uncle'); case 'fatparsib': return i18n::translate_c('father\'s parent\'s sibling', 'great-aunt/uncle'); case 'parmotsis': return i18n::translate_c('parent\'s mother\'s sister', 'great-aunt'); case 'parmotbro': return i18n::translate_c('parent\'s mother\'s brother', 'great-uncle'); case 'parmotsib': return i18n::translate_c('parent\'s mother\'s sibling', 'great-aunt/uncle'); case 'parfatsis': return i18n::translate_c('parent\'s father\'s sister', 'great-aunt'); case 'parfatbro': return i18n::translate_c('parent\'s father\'s brother', 'great-uncle'); case 'parfatsib': return i18n::translate_c('parent\'s father\'s sibling', 'great-aunt/uncle'); case 'parparsis': return i18n::translate_c('parent\'s parent\'s sister', 'great-aunt'); case 'parparbro': return i18n::translate_c('parent\'s parent\'s brother', 'great-uncle'); case 'parparsib': return i18n::translate_c('parent\'s parent\'s sibling', 'great-aunt/uncle'); case 'daudaudau': return i18n::translate_c('daughter\'s daughter\'s daughter', 'great-granddaughter'); case 'daudauson': return i18n::translate_c('daughter\'s daughter\'s son', 'great-grandson'); case 'daudauchi': return i18n::translate_c('daughter\'s daughter\'s child', 'great-grandchild'); case 'dausondau': return i18n::translate_c('daughter\'s son\'s daughter', 'great-granddaughter'); case 'dausonson': return i18n::translate_c('daughter\'s son\'s son', 'great-grandson'); case 'dausonchi': return i18n::translate_c('daughter\'s son\'s child', 'great-grandchild'); case 'dauchidau': return i18n::translate_c('daughter\'s child\'s daughter', 'great-granddaughter'); case 'dauchison': return i18n::translate_c('daughter\'s child\'s son', 'great-grandson'); case 'dauchichi': return i18n::translate_c('daughter\'s child\'s child', 'great-grandchild'); case 'sondaudau': return i18n::translate_c('son\'s daughter\'s daughter', 'great-granddaughter'); case 'sondauson': return i18n::translate_c('son\'s daughter\'s son', 'great-grandson'); case 'sondauchi': return i18n::translate_c('son\'s daughter\'s child', 'great-grandchild'); case 'sonsondau': return i18n::translate_c('son\'s son\'s daughter', 'great-granddaughter'); case 'sonsonson': return i18n::translate_c('son\'s son\'s son', 'great-grandson'); case 'sonsonchi': return i18n::translate_c('son\'s son\'s child', 'great-grandchild'); case 'sonchidau': return i18n::translate_c('son\'s child\'s daughter', 'great-granddaughter'); case 'sonchison': return i18n::translate_c('son\'s child\'s son', 'great-grandson'); case 'sonchichi': return i18n::translate_c('son\'s child\'s child', 'great-grandchild'); case 'chidaudau': return i18n::translate_c('child\'s daughter\'s daughter', 'great-granddaughter'); case 'chidauson': return i18n::translate_c('child\'s daughter\'s son', 'great-grandson'); case 'chidauchi': return i18n::translate_c('child\'s daughter\'s child', 'great-grandchild'); case 'chisondau': return i18n::translate_c('child\'s son\'s daughter', 'great-granddaughter'); case 'chisonson': return i18n::translate_c('child\'s son\'s son', 'great-grandson'); case 'chisonchi': return i18n::translate_c('child\'s son\'s child', 'great-grandchild'); case 'chichidau': return i18n::translate_c('child\'s child\'s daughter', 'great-granddaughter'); case 'chichison': return i18n::translate_c('child\'s child\'s son', 'great-grandson'); case 'chichichi': return i18n::translate_c('child\'s child\'s child', 'great-grandchild'); case 'mothusson': return i18n::translate_c('mother\'s husband\'s son', 'step-brother'); case 'mothusdau': return i18n::translate_c('mother\'s husband\'s daughter', 'step-sister'); case 'mothuschi': return i18n::translate_c('mother\'s husband\'s child', 'step-sibling'); case 'fatwifson': return i18n::translate_c('father\'s wife\'s son', 'step-brother'); case 'fatwifdau': return i18n::translate_c('father\'s wife\'s daughter', 'step-sister'); case 'fatwifchi': return i18n::translate_c('father\'s wife\'s child', 'step-sibling'); case 'parsposon': return i18n::translate_c('parent\'s spouse\'s son', 'step-brother'); case 'parspodau': return i18n::translate_c('parent\'s spouse\'s daughter', 'step-sister'); case 'parspochi': return i18n::translate_c('parent\'s spouse\'s child', 'step-sibling'); case 'motmotsis': return i18n::translate_c('mother\'s mother\'s sister', 'great-aunt'); case 'motmotbro': return i18n::translate_c('mother\'s mother\'s brother', 'great-uncle'); case 'motmotsib': return i18n::translate_c('mother\'s mother\'s sibling', 'great-aunt/uncle'); case 'motfatsis': return i18n::translate_c('mother\'s father\'s sister', 'great-aunt'); case 'motfatbro': return i18n::translate_c('mother\'s father\'s brother', 'great-uncle'); case 'motfatsib': return i18n::translate_c('mother\'s father\'s sibling', 'great-aunt/uncle'); case 'motparsis': return i18n::translate_c('mother\'s parent\'s sister', 'great-aunt'); case 'motparbro': return i18n::translate_c('mother\'s parent\'s brother', 'great-uncle'); case 'motparsib': return i18n::translate_c('mother\'s parent\'s sibling', 'great-aunt/uncle'); case 'fatmotsis': return i18n::translate_c('father\'s mother\'s sister', 'great-aunt'); case 'fatmotbro': return i18n::translate_c('father\'s mother\'s brother', 'great-uncle'); case 'fatmotsib': return i18n::translate_c('father\'s mother\'s sibling', 'great-aunt/uncle'); case 'fatfatsis': return i18n::translate_c('father\'s father\'s sister', 'great-aunt'); case 'fatfatbro': return i18n::translate_c('father\'s father\'s brother', 'great-uncle'); case 'fatfatsib': return i18n::translate_c('father\'s father\'s sibling', 'great-aunt/uncle'); case 'fatparsis': return i18n::translate_c('father\'s parent\'s sister', 'great-aunt'); case 'fatparbro': return i18n::translate_c('father\'s parent\'s brother', 'great-uncle'); case 'fatparsib': return i18n::translate_c('father\'s parent\'s sibling', 'great-aunt/uncle'); case 'parmotsis': return i18n::translate_c('parent\'s mother\'s sister', 'great-aunt'); case 'parmotbro': return i18n::translate_c('parent\'s mother\'s brother', 'great-uncle'); case 'parmotsib': return i18n::translate_c('parent\'s mother\'s sibling', 'great-aunt/uncle'); case 'parfatsis': return i18n::translate_c('parent\'s father\'s sister', 'great-aunt'); case 'parfatbro': return i18n::translate_c('parent\'s father\'s brother', 'great-uncle'); case 'parfatsib': return i18n::translate_c('parent\'s father\'s sibling', 'great-aunt/uncle'); case 'parparsis': return i18n::translate_c('parent\'s parent\'s sister', 'great-aunt'); case 'parparbro': return i18n::translate_c('parent\'s parent\'s brother', 'great-uncle'); case 'parparsib': return i18n::translate_c('parent\'s parent\'s sibling', 'great-aunt/uncle'); case 'brodauhus': return i18n::translate_c('brother\'s daughter\'s husband', 'nephew-in-law'); case 'brosonwif': return i18n::translate_c('brother\'s son\'s wife', 'niece-in-law'); case 'sisdauhus': return i18n::translate_c('sisters\'s daughter\'s husband', 'nephew-in-law'); case 'sissonwif': return i18n::translate_c('sisters\'s son\'s wife', 'niece-in-law'); case 'sibdauhus': return i18n::translate_c('sibling\'s daughter\'s husband', 'nephew-in-law'); case 'sibsonwif': return i18n::translate_c('sibling\'s son\'s wife', 'niece-in-law'); case 'motbrowif': return i18n::translate_c('mother\'s brother\'s wife', 'aunt'); case 'motsishus': return i18n::translate_c('mother\'s sister\'s husband', 'uncle'); case 'fatbrowif': return i18n::translate_c('father\'s brother\'s wife', 'aunt'); case 'fatsishus': return i18n::translate_c('father\'s sister\'s husband', 'uncle'); case 'parbrowif': return i18n::translate_c('parent\'s brother\'s wife', 'aunt'); case 'parsishus': return i18n::translate_c('parent\'s sister\'s husband', 'uncle'); case 'motbroson': return i18n::translate_c('mother\'s brother\'s son', 'cousin'); case 'motbrodau': return i18n::translate_c('mother\'s brother\'s daughter', 'cousin'); case 'motbrochi': return i18n::translate_c('mother\'s brother\'s child', 'cousin'); case 'fatbroson': return i18n::translate_c('father\'s brother\'s son', 'cousin'); case 'fatbrodau': return i18n::translate_c('father\'s brother\'s daughter', 'cousin'); case 'fatbrochi': return i18n::translate_c('father\'s brother\'s child', 'cousin'); case 'motsisson': return i18n::translate_c('mother\'s sister\'s son', 'cousin'); case 'motsisdau': return i18n::translate_c('mother\'s sister\'s daughter', 'cousin'); case 'motsischi': return i18n::translate_c('mother\'s sister\'s child', 'cousin'); case 'fatsisson': return i18n::translate_c('father\'s sister\'s son', 'cousin'); case 'fatsisdau': return i18n::translate_c('father\'s sister\'s daughter', 'cousin'); case 'fatsischi': return i18n::translate_c('father\'s sister\'s child', 'cousin'); } // Look for generic/pattern relationships. // TODO: these are heavily based on english relationship names. // We need feedback from other languages to improve this. // Dutch has special names for 8 generations of great-great-..., so these need explicit naming if (preg_match('/^((?:mot|fat|par)*)(bro|sis|sib)$/', $path, $match)) { $up=strlen($match[1])/3; $last=substr($path, -3, 3); switch ($up) { case 3: switch ($last) { case 'bro': return i18n::translate('great-great-uncle'); case 'sis': return i18n::translate('great-great-aunt'); case 'sib': return i18n::translate('great-great-aunt/uncle'); } break; case 4: switch ($last) { case 'bro': return i18n::translate('great-great-great-uncle'); case 'sis': return i18n::translate('great-great-great-aunt'); case 'sib': return i18n::translate('great-great-great-aunt/uncle'); } break; case 5: switch ($last) { case 'bro': return i18n::translate('great x4 uncle'); case 'sis': return i18n::translate('great x4 aunt'); case 'sib': return i18n::translate('great x4 aunt/uncle'); } break; case 6: switch ($last) { case 'bro': return i18n::translate('great x5 uncle'); case 'sis': return i18n::translate('great x5 aunt'); case 'sib': return i18n::translate('great x5 aunt/uncle'); } break; case 7: switch ($last) { case 'bro': return i18n::translate('great x6 uncle'); case 'sis': return i18n::translate('great x6 aunt'); case 'sib': return i18n::translate('great x6 aunt/uncle'); } break; case 8: switch ($last) { case 'bro': return i18n::translate('great x7 uncle'); case 'sis': return i18n::translate('great x7 aunt'); case 'sib': return i18n::translate('great x7 aunt/uncle'); } break; default: // Different languages have different rules for naming generations. // An english great x12 uncle is a danish great x10 uncle. // // Need to find out which languages use which rules. switch (WT_LOCALE) { case 'da': switch ($last) { case 'bro': return i18n::translate('great x%d uncle', $up-4); case 'sis': return i18n::translate('great x%d aunt', $up-4); case 'sib': return i18n::translate('great x%d aunt/uncle', $up-4); } case 'it': // Source: Michele Locati switch ($last) { case 'bro': return i18n::translate('great x%d uncle', $up-3); case 'sis': return i18n::translate('great x%d aunt', $up-3); case 'sib': return i18n::translate('great x%d aunt/uncle', $up-3); } case 'en': default: switch ($last) { case 'bro': // I18N: if you need a different number for %d, contact the developers, as a code-change is required return i18n::translate('great x%d uncle', $up-2); case 'sis': return i18n::translate('great x%d aunt', $up-2); case 'sib': return i18n::translate('great x%d aunt/uncle', $up-2); } } } } if (preg_match('/^((?:mot|fat|par)*)$/', $path, $match)) { $up=strlen($match[1])/3; $last=substr($path, -3, 3); switch ($up) { case 4: switch ($last) { case 'mot': return i18n::translate('great-great-grandmother'); case 'fat': return i18n::translate('great-great-grandfather'); case 'par': return i18n::translate('great-great-grandparent'); } break; case 5: switch ($last) { case 'mot': return i18n::translate('great-great-great-grandmother'); case 'fat': return i18n::translate('great-great-great-grandfather'); case 'par': return i18n::translate('great-great-great-grandparent'); } break; case 6: switch ($last) { case 'mot': return i18n::translate('great x4 grandmother'); case 'fat': return i18n::translate('great x4 grandfather'); case 'par': return i18n::translate('great x4 grandparent'); } break; case 7: switch ($last) { case 'mot': return i18n::translate('great x5 grandmother'); case 'fat': return i18n::translate('great x5 grandfather'); case 'par': return i18n::translate('great x5 grandparent'); } break; case 8: switch ($last) { case 'mot': return i18n::translate('great x6 grandmother'); case 'fat': return i18n::translate('great x6 grandfather'); case 'par': return i18n::translate('great x6 grandparent'); } break; case 9: switch ($last) { case 'mot': return i18n::translate('great x7 grandmother'); case 'fat': return i18n::translate('great x7 grandfather'); case 'par': return i18n::translate('great x7 grandparent'); } break; default: // Different languages have different rules for naming generations. // An english great x12 grandfather is a danish great x11 grandfather. // // Need to find out which languages use which rules. switch (WT_LOCALE) { case 'da': // Source: Patrick Sorensen switch ($last) { case 'mot': return i18n::translate('great x%d grandmother', $up-3); case 'fat': return i18n::translate('great x%d grandfather', $up-3); case 'par': return i18n::translate('great x%d grandparent', $up-3); } case 'it': // Source: Michele Locati switch ($last) { case 'mot': return i18n::translate('great x%d grandmother', $up-1); case 'fat': return i18n::translate('great x%d grandfather', $up-1); case 'par': return i18n::translate('great x%d grandparent', $up-1); } case 'en': default: switch ($last) { case 'mot': // I18N: if you need a different number for %d, contact the developers, as a code-change is required return i18n::translate('great x%d grandmother', $up-2); case 'fat': return i18n::translate('great x%d grandfather', $up-2); case 'par': return i18n::translate('great x%d grandparent', $up-2); } } } } if (preg_match('/^((?:son|dau|chi)*)$/', $path, $match)) { $up=strlen($match[1])/3; $last=substr($path, -3, 3); switch ($up) { case 4: switch ($last) { case 'son': return i18n::translate('great-great-grandson'); case 'dau': return i18n::translate('great-great-granddaughter'); case 'chi': return i18n::translate('great-great-grandchild'); } break; case 5: switch ($last) { case 'son': return i18n::translate('great-great-great-grandson'); case 'dau': return i18n::translate('great-great-great-granddaughter'); case 'chi': return i18n::translate('great-great-great-grandchild'); } break; case 6: switch ($last) { case 'son': return i18n::translate('great x4 grandson'); case 'dau': return i18n::translate('great x4 granddaughter'); case 'chi': return i18n::translate('great x4 grandchild'); } break; case 7: switch ($last) { case 'son': return i18n::translate('great x5 grandson'); case 'dau': return i18n::translate('great x5 granddaughter'); case 'chi': return i18n::translate('great x5 grandchild'); } break; case 8: switch ($last) { case 'son': return i18n::translate('great x6 grandson'); case 'dau': return i18n::translate('great x6 granddaughter'); case 'chi': return i18n::translate('great x6 grandchild'); } break; case 9: switch ($last) { case 'son': return i18n::translate('great x7 grandson'); case 'dau': return i18n::translate('great x7 granddaughter'); case 'chi': return i18n::translate('great x7 grandchild'); } break; default: // Different languages have different rules for naming generations. // An english great x12 grandson is a danish great x11 grandson. // // Need to find out which languages use which rules. switch (WT_LOCALE) { case 'da': // Source: Patrick Sorensen switch ($last) { case 'mot': return i18n::translate('great x%d grandson', $up-3); case 'fat': return i18n::translate('great x%d granddaughter', $up-3); case 'par': return i18n::translate('great x%d grandchild', $up-3); } case 'en': case 'it': // Source: Michele Locati default: switch ($last) { case 'son': // I18N: if you need a different number for %d, contact the developers, as a code-change is required return i18n::translate('great x%d grandson', $up-2); case 'dau': return i18n::translate('great x%d granddaughter', $up-2); case 'chi': return i18n::translate('great x%d grandchild', $up-2); } } } } if (preg_match('/^((?:mot|fat|par)+)(?:bro|sis|sib)((?:son|dau|chi)+)$/', $path, $match)) { $up =strlen($match[1])/3; $down=strlen($match[2])/3; $last=substr($path, -3, 3); // Different languages have different rules for naming cousins. For example, // an english "second cousin once removed" is a polish "cousin of 7th degree". // // Need to find out which languages use which rules. switch (WT_LOCALE) { case 'pl': // Source: Lucasz Wilenski switch ($last) { case 'son': return /* I18N: %s is "first", "second", ... */ i18n::translate_c('MALE', 'cousin of the %s degree', i18n::ordinal_word($up+$down+2)); case 'dau': return /* I18N: %s is "first", "second", ... */ i18n::translate_c('FEMALE', 'cousin of the %s degree', i18n::ordinal_word($up+$down+2)); case 'chi': return /* I18N: %s is "first", "second", ... */ i18n::translate('cousin of the %s degree', i18n::ordinal_word($up+$down+2)); } break; case 'it': // Source: Michele Locati. See italian_cousins_names.zip // http://webtrees.net/forums/8-translation/1200-great-xn-grandparent?limit=6&start=6 switch ($last) { case 'son': return /* I18N: %s is "first", "second", ... */ i18n::translate_c('MALE', 'cousin of the %s degree', i18n::ordinal_word($up+$down-3)); case 'dau': return /* I18N: %s is "first", "second", ... */ i18n::translate_c('FEMALE', 'cousin of the %s degree', i18n::ordinal_word($up+$down-3)); case 'chi': return /* I18N: %s is "first", "second", ... */ i18n::translate('cousin of the %s degree', i18n::ordinal_word($up+$down-3)); } break; case 'en': // See: http://en.wikipedia.org/wiki/File:CousinTree.svg default: if ($up==$down) { switch ($last) { case 'son': return i18n::translate_c('MALE', '%s cousin', i18n::ordinal_word($up-1)); case 'dau': return i18n::translate_c('FEMALE', '%s cousin', i18n::ordinal_word($up-1)); case 'chi': return i18n::translate('%s cousin', i18n::ordinal_word($up-1)); } } else { $removed=abs($down-$up); switch ($last) { case 'son': return i18n::plural( '%1$s male cousin, %2$d time removed', '%1$s male cousin, %2$d times removed', $removed, i18n::ordinal_word(min($up, $down)), $removed ); case 'dau': return i18n::plural( '%1$s female cousin, %2$d time removed', '%1$s female cousin, %2$d times removed', $removed, i18n::ordinal_word(min($up, $down)), $removed ); case 'chi': return i18n::plural('%1$s cousin, %2$d time removed', '%1$s cousin, %2$d times removed', $removed, i18n::ordinal_word(min($up, $down)), $removed ); } } break; } } // TODO: break the relationship down into sub-relationships. e.g. cousin's cousin. // We don't have a specific name for this relationship, and we can't match it with a pattern. // Just spell it out. // TODO: long relationships are a bit ridiculous - although tecnically correct. // Perhaps translate long paths as "a distant blood relative", or "a distant relative by marriage" switch (substr($path, -3, 3)) { case 'mot': $relationship=i18n::translate('mother' ); break; case 'fat': $relationship=i18n::translate('father' ); break; case 'par': $relationship=i18n::translate('parent' ); break; case 'hus': $relationship=i18n::translate('husband' ); break; case 'wif': $relationship=i18n::translate('wife' ); break; case 'spo': $relationship=i18n::translate('spouse' ); break; case 'bro': $relationship=i18n::translate('brother' ); break; case 'sis': $relationship=i18n::translate('sister' ); break; case 'sib': $relationship=i18n::translate('sibling' ); break; case 'son': $relationship=i18n::translate('son' ); break; case 'dau': $relationship=i18n::translate('daughter'); break; case 'chi': $relationship=i18n::translate('child' ); break; } while (($path=substr($path, 0, strlen($path)-3))!='') { switch (substr($path, -3, 3)) { // I18N: These strings are used to build paths of relationships, such as "father's wife's husband's brother" case 'mot': $relationship=i18n::translate('mother\'s %s', $relationship); break; case 'fat': $relationship=i18n::translate('father\'s %s', $relationship); break; case 'par': $relationship=i18n::translate('parent\'s %s', $relationship); break; case 'hus': $relationship=i18n::translate('husband\'s %s', $relationship); break; case 'wif': $relationship=i18n::translate('wife\'s %s', $relationship); break; case 'spo': $relationship=i18n::translate('spouse\'s %s', $relationship); break; case 'bro': $relationship=i18n::translate('brother\'s %s', $relationship); break; case 'sis': $relationship=i18n::translate('sister\'s %s', $relationship); break; case 'sib': $relationship=i18n::translate('sibling\'s %s', $relationship); break; case 'son': $relationship=i18n::translate('son\'s %s', $relationship); break; case 'dau': $relationship=i18n::translate('daughter\'s %s', $relationship); break; case 'chi': $relationship=i18n::translate('child\'s %s', $relationship); break; } } return $relationship; } /** * get theme names * * function to get the names of all of the themes as an array * it searches the themes directory and reads the name from the theme_name variable * in the theme.php file. * @return array and array of theme names and their corresponding directory */ function get_theme_names() { $themes = array(); $d = dir("themes"); while (false !== ($entry = $d->read())) { if ($entry{0}!="." && $entry!="CVS" && !stristr($entry, "svn") && is_dir(WT_ROOT.'themes/'.$entry) && file_exists(WT_ROOT.'themes/'.$entry.'/theme.php')) { $themefile = implode("", file(WT_ROOT.'themes/'.$entry.'/theme.php')); $tt = preg_match("/theme_name\s*=\s*\"(.*)\";/", $themefile, $match); if ($tt>0) $themename = trim($match[1]); else $themename = "themes/$entry"; $themes[$themename] = "themes/$entry/"; } } $d->close(); uksort($themes, "utf8_strcasecmp"); return $themes; } /** * decode a filename * * windows doesn't use UTF-8 for its file system so we have to decode the filename * before it can be used on the filesystem */ function filename_decode($filename) { if (DIRECTORY_SEPARATOR=='\\') return utf8_decode($filename); else return $filename; } /** * encode a filename * * windows doesn't use UTF-8 for its file system so we have to encode the filename * before it can be used in PGV */ function filename_encode($filename) { if (DIRECTORY_SEPARATOR=='\\') return utf8_encode($filename); else return $filename; } //////////////////////////////////////////////////////////////////////////////// // Remove empty and duplicate values from a URL query string //////////////////////////////////////////////////////////////////////////////// function normalize_query_string($query) { $components=array(); foreach (preg_split('/(^\?|\&(amp;)*)/', urldecode($query), -1, PREG_SPLIT_NO_EMPTY) as $component) if (strpos($component, '=')!==false) { list ($key, $data)=explode('=', $component, 2); if (!empty($data)) $components[$key]=$data; } $new_query=''; foreach ($components as $key=>$data) $new_query.=(empty($new_query)?'?':'&').$key.'='.$data; return $new_query; } function getfilesize($bytes) { if ($bytes>=1099511627776) { return round($bytes/1099511627776, 2)." TB"; } if ($bytes>=1073741824) { return round($bytes/1073741824, 2)." GB"; } if ($bytes>=1048576) { return round($bytes/1048576, 2)." MB"; } if ($bytes>=1024) { return round($bytes/1024, 2)." KB"; } return $bytes." B"; } /** * array merge function for PGV * the PHP array_merge function will reindex all numerical indexes * This function should only be used for associative arrays * @param array $array1 * @param array $array2 */ function pgv_array_merge($array1, $array2) { foreach ($array2 as $key=>$value) { $array1[$key] = $value; } return $array1; } /** * checks if the value is in an array recursively * @param string $needle * @param array $haystack */ function in_arrayr($needle, $haystack) { foreach ($haystack as $v) { if ($needle == $v) return true; else if (is_array($v)) { if (in_arrayr($needle, $v) === true) return true; } } return false; } /** * function to build an URL querystring from GET or POST variables * @return string */ function get_query_string() { $qstring = ""; if (!empty($_GET)) { foreach ($_GET as $key => $value) { if ($key != "view") { if (!is_array($value)) { $qstring .= "{$key}={$value}&"; } else { foreach ($value as $k=>$v) { $qstring .= "{$key}[{$k}]={$v}&"; } } } } } else { if (!empty($_POST)) { foreach ($_POST as $key => $value) { if ($key != "view") { if (!is_array($value)) { $qstring .= "{$key}={$value}&"; } else { foreach ($value as $k=>$v) { if (!is_array($v)) { $qstring .= "{$key}[{$k}]={$v}&"; } } } } } } } $qstring = rtrim($qstring, "&"); // Remove trailing "&" return encode_url($qstring); } //This function works with a specified generation limit. It will completely fill //the pdf witout regard to if a known person exists in each generation. //ToDo: If a known individual is found in a generation, add prior empty positions //and add remaining empty spots automatically. function add_ancestors(&$list, $pid, $children=false, $generations=-1, $show_empty=false) { $total_num_skipped = 0; $skipped_gen = 0; $num_skipped = 0; $genlist = array($pid); $list[$pid]->generation = 1; while (count($genlist)>0) { $id = array_shift($genlist); if (strpos($id, "empty")===0) continue; // id can be something like "empty7" $person = Person::getInstance($id); $famids = $person->getChildFamilies(); if (count($famids)>0) { if ($show_empty) { for ($i=0;$i<$num_skipped;$i++) { $list["empty" . $total_num_skipped] = new Person(''); $list["empty" . $total_num_skipped]->generation = $list[$id]->generation+1; array_push($genlist, "empty" . $total_num_skipped); $total_num_skipped++; } } $num_skipped = 0; foreach ($famids as $famid => $family) { $husband = $family->getHusband(); $wife = $family->getWife(); if ($husband) { $list[$husband->getXref()] = $husband; $list[$husband->getXref()]->generation = $list[$id]->generation+1; } elseif ($show_empty) { $list["empty" . $total_num_skipped] = new Person(''); $list["empty" . $total_num_skipped]->generation = $list[$id]->generation+1; } if ($wife) { $list[$wife->getXref()] = $wife; $list[$wife->getXref()]->generation = $list[$id]->generation+1; } elseif ($show_empty) { $list["empty" . $total_num_skipped] = new Person(''); $list["empty" . $total_num_skipped]->generation = $list[$id]->generation+1; } if ($generations == -1 || $list[$id]->generation+1 < $generations) { $skipped_gen = $list[$id]->generation+1; if ($husband) { array_push($genlist, $husband->getXref()); } elseif ($show_empty) { array_push($genlist, "empty" . $total_num_skipped); } if ($wife) { array_push($genlist, $wife->getXref()); } elseif ($show_empty) { array_push($genlist, "empty" . $total_num_skipped); } } $total_num_skipped++; if ($children) { $childs = $family->getChildren(); foreach($childs as $child) { $list[$child->getXref()] = $child; if (isset($list[$id]->generation)) $list[$child->getXref()]->generation = $list[$id]->generation; else $list[$child->getXref()]->generation = 1; } } } } else if ($show_empty) { if ($skipped_gen > $list[$id]->generation) { $list["empty" . $total_num_skipped] = new Person(''); $list["empty" . $total_num_skipped]->generation = $list[$id]->generation+1; $total_num_skipped++; $list["empty" . $total_num_skipped] = new Person(''); $list["empty" . $total_num_skipped]->generation = $list[$id]->generation+1; array_push($genlist, "empty" . ($total_num_skipped - 1)); array_push($genlist, "empty" . $total_num_skipped); $total_num_skipped++; } else $num_skipped += 2; } } } //--- copied from class_reportpdf.php function add_descendancy(&$list, $pid, $parents=false, $generations=-1) { $person = Person::getInstance($pid); if ($person==null) return; if (!isset($list[$pid])) { $list[$pid] = $person; } if (!isset($list[$pid]->generation)) { $list[$pid]->generation = 0; } $famids = $person->getSpouseFamilies(); if (count($famids)>0) { foreach ($famids as $famid => $family) { if ($family) { if ($parents) { $husband = $family->getHusband(); $wife = $family->getWife(); if ($husband) { $list[$husband->getXref()] = $husband; if (isset($list[$pid]->generation)) $list[$husband->getXref()]->generation = $list[$pid]->generation-1; else $list[$husband->getXref()]->generation = 1; } if ($wife) { $list[$wife->getXref()] = $wife; if (isset($list[$pid]->generation)) $list[$wife->getXref()]->generation = $list[$pid]->generation-1; else $list[$wife->getXref()]->generation = 1; } } $children = $family->getChildren(); foreach($children as $child) { if ($child) { $list[$child->getXref()] = $child; if (isset($list[$pid]->generation)) $list[$child->getXref()]->generation = $list[$pid]->generation+1; else $list[$child->getXref()]->generation = 2; } } if ($generations == -1 || $list[$pid]->generation+1 < $generations) { foreach($children as $child) { add_descendancy($list, $child->getXref(), $parents, $generations); // recurse on the childs family } } } } } } /** * check if the page view rate for a session has been exeeded. */ function CheckPageViews() { global $SEARCH_SPIDER, $MAX_VIEWS, $MAX_VIEW_TIME; if ($MAX_VIEW_TIME == 0 || $MAX_VIEWS == 0 || !empty($SEARCH_SPIDER)) return; // The media firewall should not be throttled if (WT_SCRIPT_NAME=='mediafirewall.php') return; if (!empty($_SESSION["pageviews"]["time"]) && !empty($_SESSION["pageviews"]["number"])) { $_SESSION["pageviews"]["number"] ++; if ($_SESSION["pageviews"]["number"] < $MAX_VIEWS) return; $sleepTime = $MAX_VIEW_TIME - time() + $_SESSION["pageviews"]["time"]; if ($sleepTime > 0) { // The configured page view rate has been exceeded // - Log a message and then sleep to slow things down $text = "Permitted page view rate of {$MAX_VIEWS} per {$MAX_VIEW_TIME} seconds exceeded."; AddToLog($text, 'auth'); sleep($sleepTime); } } $_SESSION["pageviews"] = array("time"=>time(), "number"=>1); } /** * get the next available xref * calculates the next available XREF id for the given type of record * @param string $type the type of record, defaults to 'INDI' * @return string */ function get_new_xref($type='INDI', $ged_id=WT_GED_ID, $use_cache=false) { global $fcontents, $SOURCE_ID_PREFIX, $REPO_ID_PREFIX, $TBLPREFIX; global $MEDIA_ID_PREFIX, $FAM_ID_PREFIX, $GEDCOM_ID_PREFIX, $MAX_IDS; $num = null; //-- check if an id is stored in MAX_IDS used mainly during the import //-- the number stored in the max_id is the next number to use... no need to increment it if ($use_cache && !empty($MAX_IDS)&& isset($MAX_IDS[$type])) { $num = 1; $num = $MAX_IDS[$type]; $MAX_IDS[$type] = $num+1; } else { //-- check for the id in the nextid table $num= WT_DB::prepare("SELECT ni_id FROM {$TBLPREFIX}nextid WHERE ni_type=? AND ni_gedfile=?") ->execute(array($type, $ged_id)) ->fetchOne(); //-- the id was not found in the table so try and find it in the file if (is_null($num) && !empty($fcontents)) { $ct = preg_match_all("/0 @(.*)@ $type/", $fcontents, $match, PREG_SET_ORDER); $num = 0; for ($i=0; $i<$ct; $i++) { $ckey = $match[$i][1]; $bt = preg_match("/(\d+)/", $ckey, $bmatch); if ($bt>0) { $bnum = trim($bmatch[1]); if ($num < $bnum) $num = $bnum; } } $num++; } //-- type wasn't found in database or in file so make a new one if (is_null($num)) { $num = 1; WT_DB::prepare("INSERT INTO {$TBLPREFIX}nextid VALUES(?, ?, ?)") ->execute(array($num+1, $type, $ged_id)); } } switch ($type) { case "INDI": $prefix = $GEDCOM_ID_PREFIX; break; case "FAM": $prefix = $FAM_ID_PREFIX; break; case "OBJE": $prefix = $MEDIA_ID_PREFIX; break; case "SOUR": $prefix = $SOURCE_ID_PREFIX; break; case "REPO": $prefix = $REPO_ID_PREFIX; break; default: $prefix = $type{0}; break; } //-- make sure this number has not already been used if ($num>=2147483647 || $num<=0) { // Popular databases are only 32 bits (signed) $num=1; } while (find_gedcom_record($prefix.$num, $ged_id) || find_gedcom_record($prefix.$num, $ged_id, true)) { ++$num; if ($num>=2147483647 || $num<=0) { // Popular databases are only 32 bits (signed) $num=1; } } //-- the key is the prefix and the number $key = $prefix.$num; //-- during the import we won't update the database at this time so return now if ($use_cache && isset($MAX_IDS[$type])) { return $key; } //-- update the next id number in the DB table WT_DB::prepare("UPDATE {$TBLPREFIX}nextid SET ni_id=? WHERE ni_type=? AND ni_gedfile=?") ->execute(array($num+1, $type, $ged_id)); return $key; } /** * check if the given string has UTF-8 characters * */ function has_utf8($string) { $len = strlen($string); for ($i=0; $i<$len; $i++) { $letter = substr($string, $i, 1); $ord = ord($letter); if ($ord==95 || $ord>=195) return true; } return false; } /** * determines whether the passed in filename is a link to an external source (i.e. contains '://') */ function isFileExternal($file) { return strpos($file, '://') !== false; } /* * Encrypt the input string * * This function is used when a file name needs to be passed to another script by means of the * GET method. This method passes parameters to the script through the URL that launches the * script. * * File names could themselves be legitimate URLs. These legitimate URLs would normally be * killed by the hacker detection code in "includes/session_spider.php". This method avoids * that problem. * */ function encrypt($string, $key='') { if (empty($key)) $key = session_id(); $result = ''; for($i=0; $i 255) $newOrd -= 256; // Make sure we stay within the 8-bit code table $result .= chr($newOrd); } $result = '*'.strtr(base64_encode($result), '+/=', '-_#'); // Avoid characters that mess up URLs return $result; } /* * Decrypt the input string * * See above. * */ function decrypt($string, $key='') { if (empty($key)) $key = session_id(); if (substr($string, 0, 1)!='*') return $string; // Input is not a valid encrypted string $string = base64_decode(strtr(substr($string, 1), '-_#', '+/=')); $result = ''; for($i=0; $i'', 'basename'=>'', 'extension'=>'', 'filename'=>''); } if (strpos($path, '/')!==false) { $tmp=explode('/', $path); $basename=end($tmp); $dirname=substr($path, 0, strlen($path) - strlen($basename) - 1); } else if (strpos($path, '\\') !== false) { $tmp=explode('\\', $path); $basename=end($tmp); $dirname=substr($path, 0, strlen($path) - strlen($basename) - 1); } else { $basename=$path; // We have just a file name $dirname='.'; // For compatibility with pathinfo() } if (strpos($basename, '.')!==false) { $tmp=explode('.', $path); $extension=end($tmp); $filename=substr($basename, 0, strlen($basename) - strlen($extension) - 1); } else { $extension=''; $filename=$basename; } return array('dirname'=>$dirname, 'basename'=>$basename, 'extension'=>$extension, 'filename'=>$filename); } // optional extra file if (file_exists(WT_ROOT.'includes/functions.extra.php')) { require WT_ROOT.'includes/functions.extra.php'; } ?>