$NODE_CACHE is an array of nodes that have been previously checked * by the relationship calculator. This cache greatly speed up the relationship privacy * checking on charts as many relationships on charts are in the same relationship path. * * See the documentation for the get_relationship() function in the functions.php file. */ $NODE_CACHE = array(); } /** * check if a person is dead * * this function will read a person's gedcom record and try to determine whether the person is * dead or not. It checks several parameters to determine death status in the following order: * 1. a DEAT record returns dead * 2. a BIRT record less than $MAX_ALIVE_AGE returns alive * 3. Any date in the record that would make them older than $MAX_ALIVE_AGE * 4. A date in the parents record that makes the parents older than $MAX_ALIVE_AGE+40 * 5. A marriage record with a date greater than $MAX_ALIVE_AGE-10 * 6. A date in the spouse record greater than $MAX_ALIVE_AGE * 7. A date in the children's record that is greater than $MAX_ALIVE_AGE-10 * 8. A date in the grand children's record that is greater than $MAX_ALIVE_AGE-30 * * This function should only be called once per individual. In index mode this is called during * the Gedcom import. In MySQL mode this is called the first time the individual is accessed * and then the database table is updated. * @author John Finlay (yalnifj) * @param string $indirec the raw gedcom record * @return bool true if dead false if alive */ function is_dead($indirec, $gedcom_id) { global $MAX_ALIVE_AGE; if (preg_match('/^0 @('.WT_REGEX_XREF.')@ INDI/', $indirec, $match)) { $pid=$match[1]; } else { return false; } // "1 DEAT Y" or "1 DEAT/2 DATE" or "1 DEAT/2 PLAC" if (preg_match('/\n1 (?:'.WT_EVENTS_DEAT.')(?: Y|(?:\n[2-9].+)*\n2 (DATE|PLAC) )/', $indirec)) { return true; } // If any event occured more than $MAX_ALIVE_AGE years ago, then assume the person is dead preg_match_all('/\n2 DATE (.+)/', $indirec, $date_matches); foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*$MAX_ALIVE_AGE) { return true; } } // If we found no dates then check the dates of close relatives. // Check parents (birth and adopted) preg_match_all('/\n1 FAMC @('.WT_REGEX_XREF.')@/', $indirec, $famc_matches); foreach ($famc_matches[1] as $famc_match) { $famrec=find_family_record($famc_match, $gedcom_id); $parents=find_parents_in_record($famrec); if (!empty($parents['HUSB'])) { preg_match_all('/\n2 DATE (.+)/', find_person_record($parents['HUSB'], $gedcom_id), $date_matches); foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); // Assume fathers are no more than 40 years older than their children if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE+40)) { return true; } } } if (!empty($parents['WIFE'])) { preg_match_all('/\n2 DATE (.+)/', find_person_record($parents['WIFE'], $gedcom_id), $date_matches); foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); // Assume mothers are no more than 40 years older than their children if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE+40)) { return true; } } } } // Check spouses preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $indirec, $fams_matches); foreach ($fams_matches[1] as $fams_match) { $famrec=find_family_record($fams_match, $gedcom_id); // Check all marriage events preg_match_all('/\n1 (?:'.WT_EVENTS_MARR.')(?:\n[2-9].+)*\n2 DATE (.+)/', $indirec, $date_matches); foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); // Assume marriage occurs after age of 10 if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE-10)) { return true; } } // Check spouse dates $parents = find_parents_in_record($famrec); if ($parents) { if ($parents['HUSB']!=$pid) { $spid = $parents['HUSB']; } else { $spid = $parents['WIFE']; } preg_match_all('/\n2 DATE (.+)/', find_person_record($spid, $gedcom_id), $date_matches); foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); // Assume max age difference between spouses of 40 years if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE+40)) { return true; } } } // Check child dates preg_match_all('/\n1 CHIL @('.WT_REGEX_XREF.')@/', $famrec, $chil_matches); foreach ($chil_matches[1] as $chil_match) { $childrec=find_person_record($chil_match, $gedcom_id); preg_match_all('/\n2 DATE (.+)/', $childrec, $date_matches); // Assume children born after age of 15 foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE-15)) { return true; } } // Check grandchildren preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $childrec, $fams2_matches); foreach ($fams2_matches[1] as $fams2_match) { preg_match_all('/\n1 CHIL @('.WT_REGEX_XREF.')@/', find_family_record($fams2_match, $gedcom_id), $chil2_matches); foreach ($chil2_matches[1] as $chil2_match) { $grandchildrec=find_person_record($chil2_match, $gedcom_id); preg_match_all('/\n2 DATE (.+)/', $grandchildrec, $date_matches); // Assume grandchildren born after age of 30 foreach ($date_matches[1] as $date_match) { $date=new GedcomDate($date_match); if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE-30)) { return true; } } } } } } return false; } /** * check if the name for a GEDCOM XRef ID should be shown * * This function uses the settings in the global variables above to determine if the current user * has sufficient privileges to access the GEDCOM resource. It first checks the * $SHOW_LIVING_NAMES variable to see if names are shown to the public. If they are * then this function will always return true. If the name is hidden then all relationships * connected with the individual are also hidden such that arriving at this record results in a dead * end. * * @author yalnifj * @param string $pid the GEDCOM XRef ID for the entity to check privacy settings for * @return boolean return true to show the person's name, return false to keep it private */ function showLivingNameById($pid) { global $SHOW_LIVING_NAMES; if ($_SESSION["wt_user"]==WT_USER_ID) { // Normal operation $pgv_GED_ID = WT_GED_ID; $pgv_USER_ACCESS_LEVEL = WT_USER_ACCESS_LEVEL; } else { // We're in the middle of a Download -- get overriding information from cache $pgv_GED_ID = $_SESSION["pgv_GED_ID"]; $pgv_USER_ACCESS_LEVEL = $_SESSION["pgv_USER_ACCESS_LEVEL"]; } return $SHOW_LIVING_NAMES>=$pgv_USER_ACCESS_LEVEL || canDisplayRecord($pgv_GED_ID, find_person_record($pid, $pgv_GED_ID)); } // Can we display a level 0 record? function canDisplayRecord($ged_id, $gedrec) { // TODO - use the privacy settings for $ged_id, not the default gedcom. global $USE_RELATIONSHIP_PRIVACY, $CHECK_MARRIAGE_RELATIONS, $MAX_RELATION_PATH_LENGTH; global $person_privacy, $person_facts, $global_facts, $HIDE_LIVE_PEOPLE, $GEDCOM, $SHOW_DEAD_PEOPLE, $MAX_ALIVE_AGE; global $PRIVACY_CHECKS, $SHOW_LIVING_NAMES, $KEEP_ALIVE_YEARS_BIRTH, $KEEP_ALIVE_YEARS_DEATH; // Only need to check each record once. static $cache; if ($cache===null) {$cache=array();} if ($_SESSION["wt_user"]==WT_USER_ID) { // Normal operation $pgv_GED_ID = WT_GED_ID; $pgv_USER_ID = WT_USER_ID; $pgv_USER_ACCESS_LEVEL = WT_USER_ACCESS_LEVEL; $pgv_USER_GEDCOM_ID = WT_USER_GEDCOM_ID; } else { // We're in the middle of a Download -- get overriding information from cache $pgv_GED_ID = $_SESSION["pgv_GED_ID"]; $pgv_USER_ID = $_SESSION["pgv_USER_ID"]; $pgv_USER_ACCESS_LEVEL = $_SESSION["pgv_USER_ACCESS_LEVEL"]; $pgv_USER_GEDCOM_ID = $_SESSION["pgv_USER_GEDCOM_ID"]; } if (preg_match('/^0 @('.WT_REGEX_XREF.')@ ('.WT_REGEX_TAG.')/', $gedrec, $match)) { $xref=$match[1]; $type=$match[2]; $cache_key="$xref@$ged_id"; if (array_key_exists($cache_key, $cache)) { return $cache[$cache_key]; } } else { // Missing data or broken link? return true; } //-- keep a count of how many times we have checked for privacy ++$PRIVACY_CHECKS; // This setting would better be called "$ENABLE_PRIVACY" if (!$HIDE_LIVE_PEOPLE) { return $cache[$cache_key]=true; } // We should always be able to see our own record if ($xref==WT_USER_GEDCOM_ID && $ged_id=WT_GED_ID) { return $cache[$cache_key]=true; } // Does this record have a RESN? if (strpos($gedrec, "\n1 RESN none")) { return $cache[$cache_key]=true; } if (strpos($gedrec, "\n1 RESN privacy")) { return $cache[$cache_key]=(WT_PRIV_USER>=$pgv_USER_ACCESS_LEVEL); } if (strpos($gedrec, "\n1 RESN confidential")) { return $cache[$cache_key]=(WT_PRIV_NONE>=$pgv_USER_ACCESS_LEVEL); } // Does this record have a default RESN? if (isset($person_privacy[$xref])) { return $cache[$cache_key]=($person_privacy[$xref]>=$pgv_USER_ACCESS_LEVEL); } // Privacy rules do not apply to admins if (WT_PRIV_NONE>=$pgv_USER_ACCESS_LEVEL) { return $cache[$cache_key]=true; } // Different types of record have different privacy rules switch ($type) { case 'INDI': // Dead people... if ($SHOW_DEAD_PEOPLE>=$pgv_USER_ACCESS_LEVEL && is_dead($gedrec, $ged_id)) { $keep_alive=false; if ($KEEP_ALIVE_YEARS_BIRTH) { preg_match_all('/\n1 (?:'.WT_EVENTS_BIRT.').*(?:\n[2-9].*)*(?:\n2 DATE (.+))/', $gedrec, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $date=new GedcomDate($match[1]); if ($date->isOK() && $date->gregorianYear()+$KEEP_ALIVE_YEARS_BIRTH > date('Y')) { $keep_alive=true; break; } } } if ($KEEP_ALIVE_YEARS_DEATH) { preg_match_all('/\n1 (?:'.WT_EVENTS_DEAT.').*(?:\n[2-9].*)*(?:\n2 DATE (.+))/', $gedrec, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $date=new GedcomDate($match[1]); if ($date->isOK() && $date->gregorianYear()+$KEEP_ALIVE_YEARS_DEATH > date('Y')) { $keep_alive=true; break; } } } if (!$keep_alive) { return $cache[$cache_key]=true; } } // Consider relationship privacy if ($pgv_USER_GEDCOM_ID && get_user_setting($pgv_USER_ID, 'relationship_privacy', $USE_RELATIONSHIP_PRIVACY)) { $path_length=get_user_setting($pgv_USER_ID, 'max_relation_path', $MAX_RELATION_PATH_LENGTH); $relationship=get_relationship($pgv_USER_GEDCOM_ID, $xref, $CHECK_MARRIAGE_RELATIONS, $path_length); return $cache[$cache_key]=($relationship!==false); } // No restriction found - show living people to authenticated users only: return WT_PRIV_USER>=$pgv_USER_ACCESS_LEVEL; case 'FAM': // Hide a family if either spouse is private if (preg_match_all('/\n1 (?:HUSB|WIFE) @('.WT_REGEX_XREF.')@/', $gedrec, $matches)) { foreach ($matches[1] as $spouse_id) { if (!canDisplayRecord($ged_id, find_person_record($spouse_id, $ged_id))) { return $cache[$cache_key]=false; } } } return true; case 'OBJE': // Hide media objects that are linked to private records foreach (get_media_relations($xref) as $gid=>$type2) { if (!canDisplayRecord($ged_id, find_gedcom_record($gid, $ged_id))) { return $cache[$cache_key]=false; } } break; case 'SOUR': // Hide sources if they are attached to private repositories. $repoid = get_gedcom_value("REPO", 1, $gedrec); if ($repoid && !canDisplayRecord($ged_id, find_other_record($repoid, $ged_id))) { return $cache[$cache_key]=false; } break; case 'NOTE': // Hide notes if they are attached to private records $linked_gids=WT_DB::prepare( "SELECT l_from FROM `##link` WHERE l_to=? AND l_file=?" )->execute(array($xref, $ged_id))->fetchOneColumn(); foreach ($linked_gids as $linked_gid) { $linked_record=GedcomRecord::getInstance($linked_gid); if (!$linked_record->canDisplayDetails()) { return $cache[$cache_key]=false; } } } // Level 1 tags (except INDI and FAM) can be controlled by global tag settings if (isset($global_facts[$type])) { return $cache[$cache_key]=($global_facts[$type]>=$pgv_USER_ACCESS_LEVEL); } // No restriction found - must be public: return $cache[$cache_key]=true; } // Can we display a level 1 record? // Assume we have already called canDisplayRecord() to check the parent level 0 object function canDisplayFact($xref, $ged_id, $gedrec) { // TODO - use the privacy settings for $ged_id, not the default gedcom. global $HIDE_LIVE_PEOPLE, $person_facts, $global_facts; // This setting would better be called "$ENABLE_PRIVACY" if (!$HIDE_LIVE_PEOPLE) { return true; } // We should always be able to see details of our own record if ($xref==WT_USER_GEDCOM_ID && $ged_id=WT_GED_ID) { return true; } // Does this record have a RESN? if (strpos($gedrec, "\n2 RESN none")) { return true; } if (strpos($gedrec, "\n2 RESN privacy")) { return WT_PRIV_USER>=WT_USER_ACCESS_LEVEL; } if (strpos($gedrec, "\n2 RESN confidential")) { return WT_PRIV_NONE>=WT_USER_ACCESS_LEVEL; } // Does this record have a default RESN? if (preg_match('/^1 ('.WT_REGEX_TAG.')/', $gedrec, $match)) { $tag=$match[1]; if (isset($person_facts[$xref][$tag])) { return $person_facts[$xref][$tag]>=WT_USER_ACCESS_LEVEL; } if (isset($global_facts[$tag])) { return $global_facts[$tag]>=WT_USER_ACCESS_LEVEL; } } // No restrictions - it must be public return true; } /** * remove all private information from a gedcom record * * this function will analyze and gedcom record and privatize it by removing all private * information that should be hidden from the user trying to access it. * @param string $gedrec the raw gedcom record to privatize * @return string the privatized gedcom record */ function privatize_gedcom($gedrec) { global $SHOW_PRIVATE_RELATIONSHIPS, $pgv_private_records; global $global_facts, $person_facts, $GEDCOM; $gedcom_id=get_id_from_gedcom($GEDCOM); if (preg_match('/^0 @('.WT_REGEX_XREF.')@ ('.WT_REGEX_TAG.')(.*)/', $gedrec, $match)) { $gid = $match[1]; $type = $match[2]; $data = $match[3]; if (canDisplayRecord($gedcom_id, $gedrec)) { // The record is not private, but the individual facts may be. if ( !strpos($gedrec, "\n2 RESN") && !isset($person_facts[$gid]) && !preg_match('/\n1 (?:'.implode('|', array_keys($global_facts)).')/', $gedrec) && !preg_match('/\n2 TYPE (?:'.implode('|', array_keys($global_facts)).')/', $gedrec) ) { // Nothing to indicate fact privacy needed return $gedrec; } $newrec="0 @{$gid}@ {$type}{$data}"; $private_record=''; // Check each of the sub facts for access if (preg_match_all('/\n1 ('.WT_REGEX_TAG.').*(?:\n[2-9].*)*/', $gedrec, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { if (($match[1]=='FACT' || $match[1]=='EVEN') && preg_match('/\n2 TYPE ([A-Z]{3,5})/', $match[0], $tmatch)) { $tag=$tmatch[1]; } else { $tag=$match[1]; } if (canDisplayFact($gid, $gedcom_id, $match[0])) { $newrec.=$match[0]; } else { $private_record.=$match[0]; } } } // Store the private data, so we can add it back in after an edit. $pgv_private_records[$gid]=$private_record; return $newrec; } else { // The whole record is private - although there are a few things we need to show. switch($type) { case 'INDI': $newrec="0 @{$gid}@ INDI"; if (showLivingNameById($gid)) { // Show all the NAME tags, including subtags if (preg_match_all('/\n1 (NAME|_HNM).*(\n[2-9].*)*/', $gedrec, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $newrec.=$match[0]; } } } else { $newrec.="\n1 NAME ".i18n::translate('Private'); } // Just show the 1 FAMC/FAMS tag, not any subtags, which may contain private data if (preg_match_all('/\n1 FAM[CS] @('.WT_REGEX_XREF.')@/', $gedrec, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { if ($SHOW_PRIVATE_RELATIONSHIPS || canDisplayRecord($gedcom_id, find_family_record($match[1], $gedcom_id))) { $newrec.=$match[0]; } } } // Don't privatize sex if (preg_match('/\n1 SEX [MFU]/', $gedrec, $match)) { $newrec.=$match[0]; } break; case 'FAM': $newrec="0 @{$gid}@ FAM"; // Just show the 1 CHIL/HUSB/WIFE tag, not any subtags, which may contain private data if (preg_match_all('/\n1 (CHIL|HUSB|WIFE) @('.WT_REGEX_XREF.')@/', $gedrec, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { if ($SHOW_PRIVATE_RELATIONSHIPS || canDisplayRecord($gedcom_id, find_person_record($match[1], $gedcom_id))) { $newrec.=$match[0]; } } } break; case 'NOTE': $newrec="0 @{$gid}@ {$type} ".i18n::translate('Private'); break; case 'SOUR': $newrec="0 @{$gid}@ {$type}\n1 TITL ".i18n::translate('Private'); break; case 'REPO': case 'SUBM': $newrec="0 @{$gid}@ {$type}\n1 NAME ".i18n::translate('Private'); break; case 'SUBN': $newrec="0 @{$gid}@ {$type}\n1 FAMF ".i18n::translate('Private'); break; case 'OBJE': default: // Other objects have no name/title, so add an inline note $newrec="0 @{$gid}@ {$type}\n1 NOTE ".i18n::translate('Private'); } return $newrec; } } else { // Invalid gedcom record, so nothing to privatize. return $gedrec; } } function get_last_private_data($gid) { global $pgv_private_records; if (!isset($pgv_private_records[$gid])) return false; return $pgv_private_records[$gid]; } /** * Check fact record for editing restrictions * * Checks if the user is allowed to change fact information, * based on the existence of the RESN tag in the fact record. * * @return int Allowed or not allowed */ function FactEditRestricted($pid, $factrec) { if (WT_USER_GEDCOM_ADMIN) { return false; } if (preg_match("/2 RESN (.*)/", $factrec, $match)) { $match[1] = strtolower(trim($match[1])); if ($match[1] == "privacy" || $match[1]=="locked") { $myindi=WT_USER_GEDCOM_ID; if ($myindi == $pid) { return false; } if (gedcom_record_type($pid, WT_GED_ID)=='FAM') { $famrec = find_family_record($pid, WT_GED_ID); $parents = find_parents_in_record($famrec); if ($myindi == $parents["HUSB"] || $myindi == $parents["WIFE"]) { return false; } } return true; } } return false; }