summaryrefslogtreecommitdiff
path: root/app/Functions/FunctionsImport.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Functions/FunctionsImport.php')
-rw-r--r--app/Functions/FunctionsImport.php2412
1 files changed, 1239 insertions, 1173 deletions
diff --git a/app/Functions/FunctionsImport.php b/app/Functions/FunctionsImport.php
index 8b47e8932b..12175c6603 100644
--- a/app/Functions/FunctionsImport.php
+++ b/app/Functions/FunctionsImport.php
@@ -34,1233 +34,1299 @@ use PDOException;
/**
* Class FunctionsImport - common functions
*/
-class FunctionsImport {
- /**
- * Tidy up a gedcom record on import, so that we can access it consistently/efficiently.
- *
- * @param string $rec
- * @param Tree $tree
- *
- * @return string
- */
- public static function reformatRecord($rec, Tree $tree) {
- // Strip out mac/msdos line endings
- $rec = preg_replace("/[\r\n]+/", "\n", $rec);
+class FunctionsImport
+{
+ /**
+ * Tidy up a gedcom record on import, so that we can access it consistently/efficiently.
+ *
+ * @param string $rec
+ * @param Tree $tree
+ *
+ * @return string
+ */
+ public static function reformatRecord($rec, Tree $tree)
+ {
+ // Strip out mac/msdos line endings
+ $rec = preg_replace("/[\r\n]+/", "\n", $rec);
- // Extract lines from the record; lines consist of: level + optional xref + tag + optional data
- $num_matches = preg_match_all('/^[ \t]*(\d+)[ \t]*(@[^@]*@)?[ \t]*(\w+)[ \t]?(.*)$/m', $rec, $matches, PREG_SET_ORDER);
+ // Extract lines from the record; lines consist of: level + optional xref + tag + optional data
+ $num_matches = preg_match_all('/^[ \t]*(\d+)[ \t]*(@[^@]*@)?[ \t]*(\w+)[ \t]?(.*)$/m', $rec, $matches, PREG_SET_ORDER);
- // Process the record line-by-line
- $newrec = '';
- foreach ($matches as $n => $match) {
- list(, $level, $xref, $tag, $data) = $match;
- $tag = strtoupper($tag); // Tags should always be upper case
- switch ($tag) {
- // Convert PhpGedView tags to WT
- case '_PGVU':
- $tag = '_WT_USER';
- break;
- case '_PGV_OBJS':
- $tag = '_WT_OBJE_SORT';
- break;
- // Convert FTM-style "TAG_FORMAL_NAME" into "TAG".
- case 'ABBREVIATION':
- $tag = 'ABBR';
- break;
- case 'ADDRESS':
- $tag = 'ADDR';
- break;
- case 'ADDRESS1':
- $tag = 'ADR1';
- break;
- case 'ADDRESS2':
- $tag = 'ADR2';
- break;
- case 'ADDRESS3':
- $tag = 'ADR3';
- break;
- case 'ADOPTION':
- $tag = 'ADOP';
- break;
- case 'ADULT_CHRISTENING':
- $tag = 'CHRA';
- break;
- case 'AFN':
- // AFN values are upper case
- $data = strtoupper($data);
- break;
- case 'AGENCY':
- $tag = 'AGNC';
- break;
- case 'ALIAS':
- $tag = 'ALIA';
- break;
- case 'ANCESTORS':
- $tag = 'ANCE';
- break;
- case 'ANCES_INTEREST':
- $tag = 'ANCI';
- break;
- case 'ANNULMENT':
- $tag = 'ANUL';
- break;
- case 'ASSOCIATES':
- $tag = 'ASSO';
- break;
- case 'AUTHOR':
- $tag = 'AUTH';
- break;
- case 'BAPTISM':
- $tag = 'BAPM';
- break;
- case 'BAPTISM_LDS':
- $tag = 'BAPL';
- break;
- case 'BAR_MITZVAH':
- $tag = 'BARM';
- break;
- case 'BAS_MITZVAH':
- $tag = 'BASM';
- break;
- case 'BIRTH':
- $tag = 'BIRT';
- break;
- case 'BLESSING':
- $tag = 'BLES';
- break;
- case 'BURIAL':
- $tag = 'BURI';
- break;
- case 'CALL_NUMBER':
- $tag = 'CALN';
- break;
- case 'CASTE':
- $tag = 'CAST';
- break;
- case 'CAUSE':
- $tag = 'CAUS';
- break;
- case 'CENSUS':
- $tag = 'CENS';
- break;
- case 'CHANGE':
- $tag = 'CHAN';
- break;
- case 'CHARACTER':
- $tag = 'CHAR';
- break;
- case 'CHILD':
- $tag = 'CHIL';
- break;
- case 'CHILDREN_COUNT':
- $tag = 'NCHI';
- break;
- case 'CHRISTENING':
- $tag = 'CHR';
- break;
- case 'CONCATENATION':
- $tag = 'CONC';
- break;
- case 'CONFIRMATION':
- $tag = 'CONF';
- break;
- case 'CONFIRMATION_LDS':
- $tag = 'CONL';
- break;
- case 'CONTINUED':
- $tag = 'CONT';
- break;
- case 'COPYRIGHT':
- $tag = 'COPR';
- break;
- case 'CORPORATE':
- $tag = 'CORP';
- break;
- case 'COUNTRY':
- $tag = 'CTRY';
- break;
- case 'CREMATION':
- $tag = 'CREM';
- break;
- case 'DATE':
- // Preserve text from INT dates
- if (strpos($data, '(') !== false) {
- list($date, $text) = explode('(', $data, 2);
- $text = ' (' . $text;
- } else {
- $date = $data;
- $text = '';
- }
- // Capitals
- $date = strtoupper($date);
- // Temporarily add leading/trailing spaces, to allow efficient matching below
- $date = " {$date} ";
- // Ensure space digits and letters
- $date = preg_replace('/([A-Z])(\d)/', '$1 $2', $date);
- $date = preg_replace('/(\d)([A-Z])/', '$1 $2', $date);
- // Ensure space before/after calendar escapes
- $date = preg_replace('/@#[^@]+@/', ' $0 ', $date);
- // "BET." => "BET"
- $date = preg_replace('/(\w\w)\./', '$1', $date);
- // "CIR" => "ABT"
- $date = str_replace(' CIR ', ' ABT ', $date);
- $date = str_replace(' APX ', ' ABT ', $date);
- // B.C. => BC (temporarily, to allow easier handling of ".")
- $date = str_replace(' B.C. ', ' BC ', $date);
- // TMG uses "EITHER X OR Y"
- $date = preg_replace('/^ EITHER (.+) OR (.+)/', ' BET $1 AND $2', $date);
- // "BET X - Y " => "BET X AND Y"
- $date = preg_replace('/^(.* BET .+) - (.+)/', '$1 AND $2', $date);
- $date = preg_replace('/^(.* FROM .+) - (.+)/', '$1 TO $2', $date);
- // "@#ESC@ FROM X TO Y" => "FROM @#ESC@ X TO @#ESC@ Y"
- $date = preg_replace('/^ +(@#[^@]+@) +FROM +(.+) +TO +(.+)/', ' FROM $1 $2 TO $1 $3', $date);
- $date = preg_replace('/^ +(@#[^@]+@) +BET +(.+) +AND +(.+)/', ' BET $1 $2 AND $1 $3', $date);
- // "@#ESC@ AFT X" => "AFT @#ESC@ X"
- $date = preg_replace('/^ +(@#[^@]+@) +(FROM|BET|TO|AND|BEF|AFT|CAL|EST|INT|ABT) +(.+)/', ' $2 $1 $3', $date);
- // Ignore any remaining punctuation, e.g. "14-MAY, 1900" => "14 MAY 1900"
- // (don't change "/" - it is used in NS/OS dates)
- $date = preg_replace('/[.,:;-]/', ' ', $date);
- // BC => B.C.
- $date = str_replace(' BC ', ' B.C. ', $date);
- // Append the "INT" text
- $data = $date . $text;
- break;
- case 'DEATH':
- $tag = 'DEAT';
- break;
- case '_DEATH_OF_SPOUSE':
- $tag = '_DETS';
- break;
- case '_DEGREE':
- $tag = '_DEG';
- break;
- case 'DESCENDANTS':
- $tag = 'DESC';
- break;
- case 'DESCENDANT_INT':
- $tag = 'DESI';
- break;
- case 'DESTINATION':
- $tag = 'DEST';
- break;
- case 'DIVORCE':
- $tag = 'DIV';
- break;
- case 'DIVORCE_FILED':
- $tag = 'DIVF';
- break;
- case 'EDUCATION':
- $tag = 'EDUC';
- break;
- case 'EMIGRATION':
- $tag = 'EMIG';
- break;
- case 'ENDOWMENT':
- $tag = 'ENDL';
- break;
- case 'ENGAGEMENT':
- $tag = 'ENGA';
- break;
- case 'EVENT':
- $tag = 'EVEN';
- break;
- case 'FACSIMILE':
- $tag = 'FAX';
- break;
- case 'FAMILY':
- $tag = 'FAM';
- break;
- case 'FAMILY_CHILD':
- $tag = 'FAMC';
- break;
- case 'FAMILY_FILE':
- $tag = 'FAMF';
- break;
- case 'FAMILY_SPOUSE':
- $tag = 'FAMS';
- break;
- case 'FIRST_COMMUNION':
- $tag = 'FCOM';
- break;
- case '_FILE':
- $tag = 'FILE';
- break;
- case 'FORMAT':
- case 'FORM':
- $tag = 'FORM';
- // Consistent commas
- $data = preg_replace('/ *, */', ', ', $data);
- break;
- case 'GEDCOM':
- $tag = 'GEDC';
- break;
- case 'GIVEN_NAME':
- $tag = 'GIVN';
- break;
- case 'GRADUATION':
- $tag = 'GRAD';
- break;
- case 'HEADER':
- case 'HEAD':
- $tag = 'HEAD';
- // HEAD records don't have an XREF or DATA
- if ($level == '0') {
- $xref = '';
- $data = '';
- }
- break;
- case 'HUSBAND':
- $tag = 'HUSB';
- break;
- case 'IDENT_NUMBER':
- $tag = 'IDNO';
- break;
- case 'IMMIGRATION':
- $tag = 'IMMI';
- break;
- case 'INDIVIDUAL':
- $tag = 'INDI';
- break;
- case 'LANGUAGE':
- $tag = 'LANG';
- break;
- case 'LATITUDE':
- $tag = 'LATI';
- break;
- case 'LONGITUDE':
- $tag = 'LONG';
- break;
- case 'MARRIAGE':
- $tag = 'MARR';
- break;
- case 'MARRIAGE_BANN':
- $tag = 'MARB';
- break;
- case 'MARRIAGE_COUNT':
- $tag = 'NMR';
- break;
- case 'MARRIAGE_CONTRACT':
- $tag = 'MARC';
- break;
- case 'MARRIAGE_LICENSE':
- $tag = 'MARL';
- break;
- case 'MARRIAGE_SETTLEMENT':
- $tag = 'MARS';
- break;
- case 'MEDIA':
- $tag = 'MEDI';
- break;
- case '_MEDICAL':
- $tag = '_MDCL';
- break;
- case '_MILITARY_SERVICE':
- $tag = '_MILT';
- break;
- case 'NAME':
- // Tidy up whitespace
- $data = preg_replace('/ +/', ' ', trim($data));
- break;
- case 'NAME_PREFIX':
- $tag = 'NPFX';
- break;
- case 'NAME_SUFFIX':
- $tag = 'NSFX';
- break;
- case 'NATIONALITY':
- $tag = 'NATI';
- break;
- case 'NATURALIZATION':
- $tag = 'NATU';
- break;
- case 'NICKNAME':
- $tag = 'NICK';
- break;
- case 'OBJECT':
- $tag = 'OBJE';
- break;
- case 'OCCUPATION':
- $tag = 'OCCU';
- break;
- case 'ORDINANCE':
- $tag = 'ORDI';
- break;
- case 'ORDINATION':
- $tag = 'ORDN';
- break;
- case 'PEDIGREE':
- case 'PEDI':
- $tag = 'PEDI';
- // PEDI values are lower case
- $data = strtolower($data);
- break;
- case 'PHONE':
- $tag = 'PHON';
- break;
- case 'PHONETIC':
- $tag = 'FONE';
- break;
- case 'PHY_DESCRIPTION':
- $tag = 'DSCR';
- break;
- case 'PLACE':
- case 'PLAC':
- $tag = 'PLAC';
- // Consistent commas
- $data = preg_replace('/ *(،|,) */', ', ', $data);
- // The Master Genealogist stores LAT/LONG data in the PLAC field, e.g. Pennsylvania, USA, 395945N0751013W
- if (preg_match('/(.*), (\d\d)(\d\d)(\d\d)([NS])(\d\d\d)(\d\d)(\d\d)([EW])$/', $data, $match)) {
- $data =
- $match[1] . "\n" .
- ($level + 1) . " MAP\n" .
- ($level + 2) . ' LATI ' . ($match[5] . (round($match[2] + ($match[3] / 60) + ($match[4] / 3600), 4))) . "\n" .
- ($level + 2) . ' LONG ' . ($match[9] . (round($match[6] + ($match[7] / 60) + ($match[8] / 3600), 4)));
- }
- break;
- case 'POSTAL_CODE':
- $tag = 'POST';
- break;
- case 'PROBATE':
- $tag = 'PROB';
- break;
- case 'PROPERTY':
- $tag = 'PROP';
- break;
- case 'PUBLICATION':
- $tag = 'PUBL';
- break;
- case 'QUALITY_OF_DATA':
- $tag = 'QUAL';
- break;
- case 'REC_FILE_NUMBER':
- $tag = 'RFN';
- break;
- case 'REC_ID_NUMBER':
- $tag = 'RIN';
- break;
- case 'REFERENCE':
- $tag = 'REFN';
- break;
- case 'RELATIONSHIP':
- $tag = 'RELA';
- break;
- case 'RELIGION':
- $tag = 'RELI';
- break;
- case 'REPOSITORY':
- $tag = 'REPO';
- break;
- case 'RESIDENCE':
- $tag = 'RESI';
- break;
- case 'RESTRICTION':
- case 'RESN':
- $tag = 'RESN';
- // RESN values are lower case (confidential, privacy, locked, none)
- $data = strtolower($data);
- if ($data == 'invisible') {
- $data = 'confidential'; // From old versions of Legacy.
- }
- break;
- case 'RETIREMENT':
- $tag = 'RETI';
- break;
- case 'ROMANIZED':
- $tag = 'ROMN';
- break;
- case 'SEALING_CHILD':
- $tag = 'SLGC';
- break;
- case 'SEALING_SPOUSE':
- $tag = 'SLGS';
- break;
- case 'SOC_SEC_NUMBER':
- $tag = 'SSN';
- break;
- case 'SEX':
- $data = strtoupper($data);
- break;
- case 'SOURCE':
- $tag = 'SOUR';
- break;
- case 'STATE':
- $tag = 'STAE';
- break;
- case 'STATUS':
- case 'STAT':
- $tag = 'STAT';
- if ($data == 'CANCELLED') {
- // PhpGedView mis-spells this tag - correct it.
- $data = 'CANCELED';
- }
- break;
- case 'SUBMISSION':
- $tag = 'SUBN';
- break;
- case 'SUBMITTER':
- $tag = 'SUBM';
- break;
- case 'SURNAME':
- $tag = 'SURN';
- break;
- case 'SURN_PREFIX':
- $tag = 'SPFX';
- break;
- case 'TEMPLE':
- case 'TEMP':
- $tag = 'TEMP';
- // Temple codes are upper case
- $data = strtoupper($data);
- break;
- case 'TITLE':
- $tag = 'TITL';
- break;
- case 'TRAILER':
- case 'TRLR':
- $tag = 'TRLR';
- // TRLR records don't have an XREF or DATA
- if ($level == '0') {
- $xref = '';
- $data = '';
- }
- break;
- case 'VERSION':
- $tag = 'VERS';
- break;
- case 'WEB':
- $tag = 'WWW';
- break;
- }
- // Suppress "Y", for facts/events with a DATE or PLAC
- if ($data == 'y') {
- $data = 'Y';
- }
- if ($level == '1' && $data == 'Y') {
- for ($i = $n + 1; $i < $num_matches - 1 && $matches[$i][1] != '1'; ++$i) {
- if ($matches[$i][3] == 'DATE' || $matches[$i][3] == 'PLAC') {
- $data = '';
- break;
- }
- }
- }
- // Reassemble components back into a single line
- switch ($tag) {
- default:
- // Remove tabs and multiple/leading/trailing spaces
- if (strpos($data, "\t") !== false) {
- $data = str_replace("\t", ' ', $data);
- }
- if (substr($data, 0, 1) == ' ' || substr($data, -1, 1) == ' ') {
- $data = trim($data);
- }
- while (strpos($data, ' ')) {
- $data = str_replace(' ', ' ', $data);
- }
- $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != 'NOTE' ? '' : ' ' . $data);
- break;
- case 'NOTE':
- case 'TEXT':
- case 'DATA':
- case 'CONT':
- $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != 'NOTE' ? '' : ' ' . $data);
- break;
- case 'FILE':
- // Strip off the user-defined path prefix
- $GEDCOM_MEDIA_PATH = $tree->getPreference('GEDCOM_MEDIA_PATH');
- if ($GEDCOM_MEDIA_PATH && strpos($data, $GEDCOM_MEDIA_PATH) === 0) {
- $data = substr($data, strlen($GEDCOM_MEDIA_PATH));
- }
- // convert backslashes in filenames to forward slashes
- $data = preg_replace("/\\\/", '/', $data);
+ // Process the record line-by-line
+ $newrec = '';
+ foreach ($matches as $n => $match) {
+ list(, $level, $xref, $tag, $data) = $match;
+ $tag = strtoupper($tag); // Tags should always be upper case
+ switch ($tag) {
+ // Convert PhpGedView tags to WT
+ case '_PGVU':
+ $tag = '_WT_USER';
+ break;
+ case '_PGV_OBJS':
+ $tag = '_WT_OBJE_SORT';
+ break;
+ // Convert FTM-style "TAG_FORMAL_NAME" into "TAG".
+ case 'ABBREVIATION':
+ $tag = 'ABBR';
+ break;
+ case 'ADDRESS':
+ $tag = 'ADDR';
+ break;
+ case 'ADDRESS1':
+ $tag = 'ADR1';
+ break;
+ case 'ADDRESS2':
+ $tag = 'ADR2';
+ break;
+ case 'ADDRESS3':
+ $tag = 'ADR3';
+ break;
+ case 'ADOPTION':
+ $tag = 'ADOP';
+ break;
+ case 'ADULT_CHRISTENING':
+ $tag = 'CHRA';
+ break;
+ case 'AFN':
+ // AFN values are upper case
+ $data = strtoupper($data);
+ break;
+ case 'AGENCY':
+ $tag = 'AGNC';
+ break;
+ case 'ALIAS':
+ $tag = 'ALIA';
+ break;
+ case 'ANCESTORS':
+ $tag = 'ANCE';
+ break;
+ case 'ANCES_INTEREST':
+ $tag = 'ANCI';
+ break;
+ case 'ANNULMENT':
+ $tag = 'ANUL';
+ break;
+ case 'ASSOCIATES':
+ $tag = 'ASSO';
+ break;
+ case 'AUTHOR':
+ $tag = 'AUTH';
+ break;
+ case 'BAPTISM':
+ $tag = 'BAPM';
+ break;
+ case 'BAPTISM_LDS':
+ $tag = 'BAPL';
+ break;
+ case 'BAR_MITZVAH':
+ $tag = 'BARM';
+ break;
+ case 'BAS_MITZVAH':
+ $tag = 'BASM';
+ break;
+ case 'BIRTH':
+ $tag = 'BIRT';
+ break;
+ case 'BLESSING':
+ $tag = 'BLES';
+ break;
+ case 'BURIAL':
+ $tag = 'BURI';
+ break;
+ case 'CALL_NUMBER':
+ $tag = 'CALN';
+ break;
+ case 'CASTE':
+ $tag = 'CAST';
+ break;
+ case 'CAUSE':
+ $tag = 'CAUS';
+ break;
+ case 'CENSUS':
+ $tag = 'CENS';
+ break;
+ case 'CHANGE':
+ $tag = 'CHAN';
+ break;
+ case 'CHARACTER':
+ $tag = 'CHAR';
+ break;
+ case 'CHILD':
+ $tag = 'CHIL';
+ break;
+ case 'CHILDREN_COUNT':
+ $tag = 'NCHI';
+ break;
+ case 'CHRISTENING':
+ $tag = 'CHR';
+ break;
+ case 'CONCATENATION':
+ $tag = 'CONC';
+ break;
+ case 'CONFIRMATION':
+ $tag = 'CONF';
+ break;
+ case 'CONFIRMATION_LDS':
+ $tag = 'CONL';
+ break;
+ case 'CONTINUED':
+ $tag = 'CONT';
+ break;
+ case 'COPYRIGHT':
+ $tag = 'COPR';
+ break;
+ case 'CORPORATE':
+ $tag = 'CORP';
+ break;
+ case 'COUNTRY':
+ $tag = 'CTRY';
+ break;
+ case 'CREMATION':
+ $tag = 'CREM';
+ break;
+ case 'DATE':
+ // Preserve text from INT dates
+ if (strpos($data, '(') !== false) {
+ list($date, $text) = explode('(', $data, 2);
+ $text = ' (' . $text;
+ } else {
+ $date = $data;
+ $text = '';
+ }
+ // Capitals
+ $date = strtoupper($date);
+ // Temporarily add leading/trailing spaces, to allow efficient matching below
+ $date = " {$date} ";
+ // Ensure space digits and letters
+ $date = preg_replace('/([A-Z])(\d)/', '$1 $2', $date);
+ $date = preg_replace('/(\d)([A-Z])/', '$1 $2', $date);
+ // Ensure space before/after calendar escapes
+ $date = preg_replace('/@#[^@]+@/', ' $0 ', $date);
+ // "BET." => "BET"
+ $date = preg_replace('/(\w\w)\./', '$1', $date);
+ // "CIR" => "ABT"
+ $date = str_replace(' CIR ', ' ABT ', $date);
+ $date = str_replace(' APX ', ' ABT ', $date);
+ // B.C. => BC (temporarily, to allow easier handling of ".")
+ $date = str_replace(' B.C. ', ' BC ', $date);
+ // TMG uses "EITHER X OR Y"
+ $date = preg_replace('/^ EITHER (.+) OR (.+)/', ' BET $1 AND $2', $date);
+ // "BET X - Y " => "BET X AND Y"
+ $date = preg_replace('/^(.* BET .+) - (.+)/', '$1 AND $2', $date);
+ $date = preg_replace('/^(.* FROM .+) - (.+)/', '$1 TO $2', $date);
+ // "@#ESC@ FROM X TO Y" => "FROM @#ESC@ X TO @#ESC@ Y"
+ $date = preg_replace('/^ +(@#[^@]+@) +FROM +(.+) +TO +(.+)/', ' FROM $1 $2 TO $1 $3', $date);
+ $date = preg_replace('/^ +(@#[^@]+@) +BET +(.+) +AND +(.+)/', ' BET $1 $2 AND $1 $3', $date);
+ // "@#ESC@ AFT X" => "AFT @#ESC@ X"
+ $date = preg_replace('/^ +(@#[^@]+@) +(FROM|BET|TO|AND|BEF|AFT|CAL|EST|INT|ABT) +(.+)/', ' $2 $1 $3', $date);
+ // Ignore any remaining punctuation, e.g. "14-MAY, 1900" => "14 MAY 1900"
+ // (don't change "/" - it is used in NS/OS dates)
+ $date = preg_replace('/[.,:;-]/', ' ', $date);
+ // BC => B.C.
+ $date = str_replace(' BC ', ' B.C. ', $date);
+ // Append the "INT" text
+ $data = $date . $text;
+ break;
+ case 'DEATH':
+ $tag = 'DEAT';
+ break;
+ case '_DEATH_OF_SPOUSE':
+ $tag = '_DETS';
+ break;
+ case '_DEGREE':
+ $tag = '_DEG';
+ break;
+ case 'DESCENDANTS':
+ $tag = 'DESC';
+ break;
+ case 'DESCENDANT_INT':
+ $tag = 'DESI';
+ break;
+ case 'DESTINATION':
+ $tag = 'DEST';
+ break;
+ case 'DIVORCE':
+ $tag = 'DIV';
+ break;
+ case 'DIVORCE_FILED':
+ $tag = 'DIVF';
+ break;
+ case 'EDUCATION':
+ $tag = 'EDUC';
+ break;
+ case 'EMIGRATION':
+ $tag = 'EMIG';
+ break;
+ case 'ENDOWMENT':
+ $tag = 'ENDL';
+ break;
+ case 'ENGAGEMENT':
+ $tag = 'ENGA';
+ break;
+ case 'EVENT':
+ $tag = 'EVEN';
+ break;
+ case 'FACSIMILE':
+ $tag = 'FAX';
+ break;
+ case 'FAMILY':
+ $tag = 'FAM';
+ break;
+ case 'FAMILY_CHILD':
+ $tag = 'FAMC';
+ break;
+ case 'FAMILY_FILE':
+ $tag = 'FAMF';
+ break;
+ case 'FAMILY_SPOUSE':
+ $tag = 'FAMS';
+ break;
+ case 'FIRST_COMMUNION':
+ $tag = 'FCOM';
+ break;
+ case '_FILE':
+ $tag = 'FILE';
+ break;
+ case 'FORMAT':
+ case 'FORM':
+ $tag = 'FORM';
+ // Consistent commas
+ $data = preg_replace('/ *, */', ', ', $data);
+ break;
+ case 'GEDCOM':
+ $tag = 'GEDC';
+ break;
+ case 'GIVEN_NAME':
+ $tag = 'GIVN';
+ break;
+ case 'GRADUATION':
+ $tag = 'GRAD';
+ break;
+ case 'HEADER':
+ case 'HEAD':
+ $tag = 'HEAD';
+ // HEAD records don't have an XREF or DATA
+ if ($level == '0') {
+ $xref = '';
+ $data = '';
+ }
+ break;
+ case 'HUSBAND':
+ $tag = 'HUSB';
+ break;
+ case 'IDENT_NUMBER':
+ $tag = 'IDNO';
+ break;
+ case 'IMMIGRATION':
+ $tag = 'IMMI';
+ break;
+ case 'INDIVIDUAL':
+ $tag = 'INDI';
+ break;
+ case 'LANGUAGE':
+ $tag = 'LANG';
+ break;
+ case 'LATITUDE':
+ $tag = 'LATI';
+ break;
+ case 'LONGITUDE':
+ $tag = 'LONG';
+ break;
+ case 'MARRIAGE':
+ $tag = 'MARR';
+ break;
+ case 'MARRIAGE_BANN':
+ $tag = 'MARB';
+ break;
+ case 'MARRIAGE_COUNT':
+ $tag = 'NMR';
+ break;
+ case 'MARRIAGE_CONTRACT':
+ $tag = 'MARC';
+ break;
+ case 'MARRIAGE_LICENSE':
+ $tag = 'MARL';
+ break;
+ case 'MARRIAGE_SETTLEMENT':
+ $tag = 'MARS';
+ break;
+ case 'MEDIA':
+ $tag = 'MEDI';
+ break;
+ case '_MEDICAL':
+ $tag = '_MDCL';
+ break;
+ case '_MILITARY_SERVICE':
+ $tag = '_MILT';
+ break;
+ case 'NAME':
+ // Tidy up whitespace
+ $data = preg_replace('/ +/', ' ', trim($data));
+ break;
+ case 'NAME_PREFIX':
+ $tag = 'NPFX';
+ break;
+ case 'NAME_SUFFIX':
+ $tag = 'NSFX';
+ break;
+ case 'NATIONALITY':
+ $tag = 'NATI';
+ break;
+ case 'NATURALIZATION':
+ $tag = 'NATU';
+ break;
+ case 'NICKNAME':
+ $tag = 'NICK';
+ break;
+ case 'OBJECT':
+ $tag = 'OBJE';
+ break;
+ case 'OCCUPATION':
+ $tag = 'OCCU';
+ break;
+ case 'ORDINANCE':
+ $tag = 'ORDI';
+ break;
+ case 'ORDINATION':
+ $tag = 'ORDN';
+ break;
+ case 'PEDIGREE':
+ case 'PEDI':
+ $tag = 'PEDI';
+ // PEDI values are lower case
+ $data = strtolower($data);
+ break;
+ case 'PHONE':
+ $tag = 'PHON';
+ break;
+ case 'PHONETIC':
+ $tag = 'FONE';
+ break;
+ case 'PHY_DESCRIPTION':
+ $tag = 'DSCR';
+ break;
+ case 'PLACE':
+ case 'PLAC':
+ $tag = 'PLAC';
+ // Consistent commas
+ $data = preg_replace('/ *(،|,) */', ', ', $data);
+ // The Master Genealogist stores LAT/LONG data in the PLAC field, e.g. Pennsylvania, USA, 395945N0751013W
+ if (preg_match('/(.*), (\d\d)(\d\d)(\d\d)([NS])(\d\d\d)(\d\d)(\d\d)([EW])$/', $data, $match)) {
+ $data =
+ $match[1] . "\n" .
+ ($level + 1) . " MAP\n" .
+ ($level + 2) . ' LATI ' . ($match[5] . (round($match[2] + ($match[3] / 60) + ($match[4] / 3600), 4))) . "\n" .
+ ($level + 2) . ' LONG ' . ($match[9] . (round($match[6] + ($match[7] / 60) + ($match[8] / 3600), 4)));
+ }
+ break;
+ case 'POSTAL_CODE':
+ $tag = 'POST';
+ break;
+ case 'PROBATE':
+ $tag = 'PROB';
+ break;
+ case 'PROPERTY':
+ $tag = 'PROP';
+ break;
+ case 'PUBLICATION':
+ $tag = 'PUBL';
+ break;
+ case 'QUALITY_OF_DATA':
+ $tag = 'QUAL';
+ break;
+ case 'REC_FILE_NUMBER':
+ $tag = 'RFN';
+ break;
+ case 'REC_ID_NUMBER':
+ $tag = 'RIN';
+ break;
+ case 'REFERENCE':
+ $tag = 'REFN';
+ break;
+ case 'RELATIONSHIP':
+ $tag = 'RELA';
+ break;
+ case 'RELIGION':
+ $tag = 'RELI';
+ break;
+ case 'REPOSITORY':
+ $tag = 'REPO';
+ break;
+ case 'RESIDENCE':
+ $tag = 'RESI';
+ break;
+ case 'RESTRICTION':
+ case 'RESN':
+ $tag = 'RESN';
+ // RESN values are lower case (confidential, privacy, locked, none)
+ $data = strtolower($data);
+ if ($data == 'invisible') {
+ $data = 'confidential'; // From old versions of Legacy.
+ }
+ break;
+ case 'RETIREMENT':
+ $tag = 'RETI';
+ break;
+ case 'ROMANIZED':
+ $tag = 'ROMN';
+ break;
+ case 'SEALING_CHILD':
+ $tag = 'SLGC';
+ break;
+ case 'SEALING_SPOUSE':
+ $tag = 'SLGS';
+ break;
+ case 'SOC_SEC_NUMBER':
+ $tag = 'SSN';
+ break;
+ case 'SEX':
+ $data = strtoupper($data);
+ break;
+ case 'SOURCE':
+ $tag = 'SOUR';
+ break;
+ case 'STATE':
+ $tag = 'STAE';
+ break;
+ case 'STATUS':
+ case 'STAT':
+ $tag = 'STAT';
+ if ($data == 'CANCELLED') {
+ // PhpGedView mis-spells this tag - correct it.
+ $data = 'CANCELED';
+ }
+ break;
+ case 'SUBMISSION':
+ $tag = 'SUBN';
+ break;
+ case 'SUBMITTER':
+ $tag = 'SUBM';
+ break;
+ case 'SURNAME':
+ $tag = 'SURN';
+ break;
+ case 'SURN_PREFIX':
+ $tag = 'SPFX';
+ break;
+ case 'TEMPLE':
+ case 'TEMP':
+ $tag = 'TEMP';
+ // Temple codes are upper case
+ $data = strtoupper($data);
+ break;
+ case 'TITLE':
+ $tag = 'TITL';
+ break;
+ case 'TRAILER':
+ case 'TRLR':
+ $tag = 'TRLR';
+ // TRLR records don't have an XREF or DATA
+ if ($level == '0') {
+ $xref = '';
+ $data = '';
+ }
+ break;
+ case 'VERSION':
+ $tag = 'VERS';
+ break;
+ case 'WEB':
+ $tag = 'WWW';
+ break;
+ }
+ // Suppress "Y", for facts/events with a DATE or PLAC
+ if ($data == 'y') {
+ $data = 'Y';
+ }
+ if ($level == '1' && $data == 'Y') {
+ for ($i = $n + 1; $i < $num_matches - 1 && $matches[$i][1] != '1'; ++$i) {
+ if ($matches[$i][3] == 'DATE' || $matches[$i][3] == 'PLAC') {
+ $data = '';
+ break;
+ }
+ }
+ }
+ // Reassemble components back into a single line
+ switch ($tag) {
+ default:
+ // Remove tabs and multiple/leading/trailing spaces
+ if (strpos($data, "\t") !== false) {
+ $data = str_replace("\t", ' ', $data);
+ }
+ if (substr($data, 0, 1) == ' ' || substr($data, -1, 1) == ' ') {
+ $data = trim($data);
+ }
+ while (strpos($data, ' ')) {
+ $data = str_replace(' ', ' ', $data);
+ }
+ $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != 'NOTE' ? '' : ' ' . $data);
+ break;
+ case 'NOTE':
+ case 'TEXT':
+ case 'DATA':
+ case 'CONT':
+ $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != 'NOTE' ? '' : ' ' . $data);
+ break;
+ case 'FILE':
+ // Strip off the user-defined path prefix
+ $GEDCOM_MEDIA_PATH = $tree->getPreference('GEDCOM_MEDIA_PATH');
+ if ($GEDCOM_MEDIA_PATH && strpos($data, $GEDCOM_MEDIA_PATH) === 0) {
+ $data = substr($data, strlen($GEDCOM_MEDIA_PATH));
+ }
+ // convert backslashes in filenames to forward slashes
+ $data = preg_replace("/\\\/", '/', $data);
- $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != 'NOTE' ? '' : ' ' . $data);
- break;
- case 'CONC':
- // Merge CONC lines, to simplify access later on.
- $newrec .= ($tree->getPreference('WORD_WRAPPED_NOTES') ? ' ' : '') . $data;
- break;
- }
- }
+ $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != 'NOTE' ? '' : ' ' . $data);
+ break;
+ case 'CONC':
+ // Merge CONC lines, to simplify access later on.
+ $newrec .= ($tree->getPreference('WORD_WRAPPED_NOTES') ? ' ' : '') . $data;
+ break;
+ }
+ }
- return $newrec;
- }
+ return $newrec;
+ }
- /**
- * import record into database
- *
- * this function will parse the given gedcom record and add it to the database
- *
- * @param string $gedrec the raw gedcom record to parse
- * @param Tree $tree import the record into this tree
- * @param bool $update whether or not this is an updated record that has been accepted
- */
- public static function importRecord($gedrec, Tree $tree, $update) {
- $tree_id = $tree->getTreeId();
+ /**
+ * import record into database
+ *
+ * this function will parse the given gedcom record and add it to the database
+ *
+ * @param string $gedrec the raw gedcom record to parse
+ * @param Tree $tree import the record into this tree
+ * @param bool $update whether or not this is an updated record that has been accepted
+ */
+ public static function importRecord($gedrec, Tree $tree, $update)
+ {
+ $tree_id = $tree->getTreeId();
- // Escaped @ signs (only if importing from file)
- if (!$update) {
- $gedrec = str_replace('@@', '@', $gedrec);
- }
+ // Escaped @ signs (only if importing from file)
+ if (!$update) {
+ $gedrec = str_replace('@@', '@', $gedrec);
+ }
- // Standardise gedcom format
- $gedrec = self::reformatRecord($gedrec, $tree);
+ // Standardise gedcom format
+ $gedrec = self::reformatRecord($gedrec, $tree);
- // import different types of records
- if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedrec, $match)) {
- list(, $xref, $type) = $match;
- // check for a _UID, if the record doesn't have one, add one
- if ($tree->getPreference('GENERATE_UIDS') && !strpos($gedrec, "\n1 _UID ")) {
- $gedrec .= "\n1 _UID " . GedcomTag::createUid();
- }
- } elseif (preg_match('/0 (HEAD|TRLR)/', $gedrec, $match)) {
- $type = $match[1];
- $xref = $type; // For HEAD/TRLR, use type as pseudo XREF.
- } else {
- echo I18N::translate('Invalid GEDCOM format'), '<br><pre>', $gedrec, '</pre>';
+ // import different types of records
+ if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedrec, $match)) {
+ list(, $xref, $type) = $match;
+ // check for a _UID, if the record doesn't have one, add one
+ if ($tree->getPreference('GENERATE_UIDS') && !strpos($gedrec, "\n1 _UID ")) {
+ $gedrec .= "\n1 _UID " . GedcomTag::createUid();
+ }
+ } elseif (preg_match('/0 (HEAD|TRLR)/', $gedrec, $match)) {
+ $type = $match[1];
+ $xref = $type; // For HEAD/TRLR, use type as pseudo XREF.
+ } else {
+ echo I18N::translate('Invalid GEDCOM format'), '<br><pre>', $gedrec, '</pre>';
- return;
- }
+ return;
+ }
- // If the user has downloaded their GEDCOM data (containing media objects) and edited it
- // using an application which does not support (and deletes) media objects, then add them
- // back in.
- if ($tree->getPreference('keep_media') && $xref) {
- $old_linked_media =
- Database::prepare("SELECT l_to FROM `##link` WHERE l_from=? AND l_file=? AND l_type='OBJE'")
- ->execute([$xref, $tree_id])
- ->fetchOneColumn();
- foreach ($old_linked_media as $media_id) {
- $gedrec .= "\n1 OBJE @" . $media_id . '@';
- }
- }
+ // If the user has downloaded their GEDCOM data (containing media objects) and edited it
+ // using an application which does not support (and deletes) media objects, then add them
+ // back in.
+ if ($tree->getPreference('keep_media') && $xref) {
+ $old_linked_media =
+ Database::prepare("SELECT l_to FROM `##link` WHERE l_from=? AND l_file=? AND l_type='OBJE'")
+ ->execute([
+ $xref,
+ $tree_id,
+ ])
+ ->fetchOneColumn();
+ foreach ($old_linked_media as $media_id) {
+ $gedrec .= "\n1 OBJE @" . $media_id . '@';
+ }
+ }
- switch ($type) {
- case 'INDI':
- // Convert inline media into media objects
- $gedrec = self::convertInlineMedia($tree, $gedrec);
+ switch ($type) {
+ case 'INDI':
+ // Convert inline media into media objects
+ $gedrec = self::convertInlineMedia($tree, $gedrec);
- $record = new Individual($xref, $gedrec, null, $tree);
- if (preg_match('/\n1 RIN (.+)/', $gedrec, $match)) {
- $rin = $match[1];
- } else {
- $rin = $xref;
- }
- Database::prepare(
- "INSERT INTO `##individuals` (i_id, i_file, i_rin, i_sex, i_gedcom) VALUES (?, ?, ?, ?, ?)"
- )->execute([
- $xref, $tree_id, $rin, $record->getSex(), $gedrec,
- ]);
- // Update the cross-reference/index tables.
- self::updatePlaces($xref, $tree_id, $gedrec);
- self::updateDates($xref, $tree_id, $gedrec);
- self::updateLinks($xref, $tree_id, $gedrec);
- self::updateNames($xref, $tree_id, $record);
- break;
- case 'FAM':
- // Convert inline media into media objects
- $gedrec = self::convertInlineMedia($tree, $gedrec);
+ $record = new Individual($xref, $gedrec, null, $tree);
+ if (preg_match('/\n1 RIN (.+)/', $gedrec, $match)) {
+ $rin = $match[1];
+ } else {
+ $rin = $xref;
+ }
+ Database::prepare(
+ "INSERT INTO `##individuals` (i_id, i_file, i_rin, i_sex, i_gedcom) VALUES (?, ?, ?, ?, ?)"
+ )->execute([
+ $xref,
+ $tree_id,
+ $rin,
+ $record->getSex(),
+ $gedrec,
+ ]);
+ // Update the cross-reference/index tables.
+ self::updatePlaces($xref, $tree_id, $gedrec);
+ self::updateDates($xref, $tree_id, $gedrec);
+ self::updateLinks($xref, $tree_id, $gedrec);
+ self::updateNames($xref, $tree_id, $record);
+ break;
+ case 'FAM':
+ // Convert inline media into media objects
+ $gedrec = self::convertInlineMedia($tree, $gedrec);
- if (preg_match('/\n1 HUSB @(' . WT_REGEX_XREF . ')@/', $gedrec, $match)) {
- $husb = $match[1];
- } else {
- $husb = '';
- }
- if (preg_match('/\n1 WIFE @(' . WT_REGEX_XREF . ')@/', $gedrec, $match)) {
- $wife = $match[1];
- } else {
- $wife = '';
- }
- $nchi = preg_match_all('/\n1 CHIL @(' . WT_REGEX_XREF . ')@/', $gedrec, $match);
- if (preg_match('/\n1 NCHI (\d+)/', $gedrec, $match)) {
- $nchi = max($nchi, $match[1]);
- }
- Database::prepare(
- "INSERT INTO `##families` (f_id, f_file, f_husb, f_wife, f_gedcom, f_numchil) VALUES (?, ?, ?, ?, ?, ?)"
- )->execute([
- $xref, $tree_id, $husb, $wife, $gedrec, $nchi,
- ]);
- // Update the cross-reference/index tables.
- self::updatePlaces($xref, $tree_id, $gedrec);
- self::updateDates($xref, $tree_id, $gedrec);
- self::updateLinks($xref, $tree_id, $gedrec);
- break;
- case 'SOUR':
- // Convert inline media into media objects
- $gedrec = self::convertInlineMedia($tree, $gedrec);
+ if (preg_match('/\n1 HUSB @(' . WT_REGEX_XREF . ')@/', $gedrec, $match)) {
+ $husb = $match[1];
+ } else {
+ $husb = '';
+ }
+ if (preg_match('/\n1 WIFE @(' . WT_REGEX_XREF . ')@/', $gedrec, $match)) {
+ $wife = $match[1];
+ } else {
+ $wife = '';
+ }
+ $nchi = preg_match_all('/\n1 CHIL @(' . WT_REGEX_XREF . ')@/', $gedrec, $match);
+ if (preg_match('/\n1 NCHI (\d+)/', $gedrec, $match)) {
+ $nchi = max($nchi, $match[1]);
+ }
+ Database::prepare(
+ "INSERT INTO `##families` (f_id, f_file, f_husb, f_wife, f_gedcom, f_numchil) VALUES (?, ?, ?, ?, ?, ?)"
+ )->execute([
+ $xref,
+ $tree_id,
+ $husb,
+ $wife,
+ $gedrec,
+ $nchi,
+ ]);
+ // Update the cross-reference/index tables.
+ self::updatePlaces($xref, $tree_id, $gedrec);
+ self::updateDates($xref, $tree_id, $gedrec);
+ self::updateLinks($xref, $tree_id, $gedrec);
+ break;
+ case 'SOUR':
+ // Convert inline media into media objects
+ $gedrec = self::convertInlineMedia($tree, $gedrec);
- $record = new Source($xref, $gedrec, null, $tree);
- if (preg_match('/\n1 TITL (.+)/', $gedrec, $match)) {
- $name = $match[1];
- } elseif (preg_match('/\n1 ABBR (.+)/', $gedrec, $match)) {
- $name = $match[1];
- } else {
- $name = $xref;
- }
- Database::prepare(
- "INSERT INTO `##sources` (s_id, s_file, s_name, s_gedcom) VALUES (?, ?, LEFT(?, 255), ?)"
- )->execute([
- $xref, $tree_id, $name, $gedrec,
- ]);
- // Update the cross-reference/index tables.
- self::updateLinks($xref, $tree_id, $gedrec);
- self::updateNames($xref, $tree_id, $record);
- break;
- case 'REPO':
- // Convert inline media into media objects
- $gedrec = self::convertInlineMedia($tree, $gedrec);
+ $record = new Source($xref, $gedrec, null, $tree);
+ if (preg_match('/\n1 TITL (.+)/', $gedrec, $match)) {
+ $name = $match[1];
+ } elseif (preg_match('/\n1 ABBR (.+)/', $gedrec, $match)) {
+ $name = $match[1];
+ } else {
+ $name = $xref;
+ }
+ Database::prepare(
+ "INSERT INTO `##sources` (s_id, s_file, s_name, s_gedcom) VALUES (?, ?, LEFT(?, 255), ?)"
+ )->execute([
+ $xref,
+ $tree_id,
+ $name,
+ $gedrec,
+ ]);
+ // Update the cross-reference/index tables.
+ self::updateLinks($xref, $tree_id, $gedrec);
+ self::updateNames($xref, $tree_id, $record);
+ break;
+ case 'REPO':
+ // Convert inline media into media objects
+ $gedrec = self::convertInlineMedia($tree, $gedrec);
- $record = new Repository($xref, $gedrec, null, $tree);
- Database::prepare(
- "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, 'REPO', ?)"
- )->execute([
- $xref, $tree_id, $gedrec,
- ]);
- // Update the cross-reference/index tables.
- self::updateLinks($xref, $tree_id, $gedrec);
- self::updateNames($xref, $tree_id, $record);
- break;
- case 'NOTE':
- $record = new Note($xref, $gedrec, null, $tree);
- Database::prepare(
- "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, 'NOTE', ?)"
- )->execute([
- $xref, $tree_id, $gedrec,
- ]);
- // Update the cross-reference/index tables.
- self::updateLinks($xref, $tree_id, $gedrec);
- self::updateNames($xref, $tree_id, $record);
- break;
- case 'OBJE':
- $record = new Media($xref, $gedrec, null, $tree);
- Database::prepare(
- "INSERT INTO `##media` (m_id, m_file, m_gedcom) VALUES (:m_id, :m_file, :m_gedcom)"
- )->execute([
- 'm_id' => $xref,
- 'm_file' => $tree_id,
- 'm_gedcom' => $gedrec,
- ]);
+ $record = new Repository($xref, $gedrec, null, $tree);
+ Database::prepare(
+ "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, 'REPO', ?)"
+ )->execute([
+ $xref,
+ $tree_id,
+ $gedrec,
+ ]);
+ // Update the cross-reference/index tables.
+ self::updateLinks($xref, $tree_id, $gedrec);
+ self::updateNames($xref, $tree_id, $record);
+ break;
+ case 'NOTE':
+ $record = new Note($xref, $gedrec, null, $tree);
+ Database::prepare(
+ "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, 'NOTE', ?)"
+ )->execute([
+ $xref,
+ $tree_id,
+ $gedrec,
+ ]);
+ // Update the cross-reference/index tables.
+ self::updateLinks($xref, $tree_id, $gedrec);
+ self::updateNames($xref, $tree_id, $record);
+ break;
+ case 'OBJE':
+ $record = new Media($xref, $gedrec, null, $tree);
+ Database::prepare(
+ "INSERT INTO `##media` (m_id, m_file, m_gedcom) VALUES (:m_id, :m_file, :m_gedcom)"
+ )->execute([
+ 'm_id' => $xref,
+ 'm_file' => $tree_id,
+ 'm_gedcom' => $gedrec,
+ ]);
- foreach ($record->mediaFiles() as $media_file) {
- Database::prepare(
- "INSERT INTO `##media_file` (m_id, m_file, multimedia_file_refn, multimedia_format, source_media_type, descriptive_title) VALUES (:m_id, :m_file, LEFT(:multimedia_file_refn, 512), LEFT(:multimedia_format, 4), LEFT(:source_media_type, 15), LEFT(:descriptive_title, 248))"
- )->execute([
- 'm_id' => $xref,
- 'm_file' => $tree_id,
- 'multimedia_file_refn' => $media_file->filename(),
- 'multimedia_format' => $media_file->format(),
- 'source_media_type' => $media_file->type(),
- 'descriptive_title' => $media_file->title(),
- ]);
- }
+ foreach ($record->mediaFiles() as $media_file) {
+ Database::prepare(
+ "INSERT INTO `##media_file` (m_id, m_file, multimedia_file_refn, multimedia_format, source_media_type, descriptive_title) VALUES (:m_id, :m_file, LEFT(:multimedia_file_refn, 512), LEFT(:multimedia_format, 4), LEFT(:source_media_type, 15), LEFT(:descriptive_title, 248))"
+ )->execute([
+ 'm_id' => $xref,
+ 'm_file' => $tree_id,
+ 'multimedia_file_refn' => $media_file->filename(),
+ 'multimedia_format' => $media_file->format(),
+ 'source_media_type' => $media_file->type(),
+ 'descriptive_title' => $media_file->title(),
+ ]);
+ }
- // Update the cross-reference/index tables.
- self::updateLinks($xref, $tree_id, $gedrec);
- self::updateNames($xref, $tree_id, $record);
- break;
- default: // HEAD, TRLR, SUBM, SUBN, and custom record types.
- // Force HEAD records to have a creation date.
- if ($type === 'HEAD' && strpos($gedrec, "\n1 DATE ") === false) {
- $gedrec .= "\n1 DATE " . date('j M Y');
- }
- Database::prepare(
- "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, LEFT(?, 15), ?)"
- )->execute([$xref, $tree_id, $type, $gedrec]);
- // Update the cross-reference/index tables.
- self::updateLinks($xref, $tree_id, $gedrec);
- break;
- }
- }
+ // Update the cross-reference/index tables.
+ self::updateLinks($xref, $tree_id, $gedrec);
+ self::updateNames($xref, $tree_id, $record);
+ break;
+ default: // HEAD, TRLR, SUBM, SUBN, and custom record types.
+ // Force HEAD records to have a creation date.
+ if ($type === 'HEAD' && strpos($gedrec, "\n1 DATE ") === false) {
+ $gedrec .= "\n1 DATE " . date('j M Y');
+ }
+ Database::prepare(
+ "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, LEFT(?, 15), ?)"
+ )->execute([
+ $xref,
+ $tree_id,
+ $type,
+ $gedrec,
+ ]);
+ // Update the cross-reference/index tables.
+ self::updateLinks($xref, $tree_id, $gedrec);
+ break;
+ }
+ }
- /**
- * extract all places from the given record and insert them into the places table
- *
- * @param string $gid
- * @param int $ged_id
- * @param string $gedrec
- */
- public static function updatePlaces($gid, $ged_id, $gedrec) {
- global $placecache;
+ /**
+ * extract all places from the given record and insert them into the places table
+ *
+ * @param string $gid
+ * @param int $ged_id
+ * @param string $gedrec
+ */
+ public static function updatePlaces($gid, $ged_id, $gedrec)
+ {
+ global $placecache;
- if (!isset($placecache)) {
- $placecache = [];
- }
- $personplace = [];
- // import all place locations, but not control info such as
- // 0 HEAD/1 PLAC or 0 _EVDEF/1 PLAC
- $pt = preg_match_all('/^[2-9] PLAC (.+)/m', $gedrec, $match, PREG_SET_ORDER);
- for ($i = 0; $i < $pt; $i++) {
- $place = trim($match[$i][1]);
- $lowplace = I18N::strtolower($place);
- //-- if we have already visited this place for this person then we don't need to again
- if (isset($personplace[$lowplace])) {
- continue;
- }
- $personplace[$lowplace] = 1;
- $places = explode(',', $place);
- //-- reverse the array to start at the highest level
- $secalp = array_reverse($places);
- $parent_id = 0;
- $search = true;
+ if (!isset($placecache)) {
+ $placecache = [];
+ }
+ $personplace = [];
+ // import all place locations, but not control info such as
+ // 0 HEAD/1 PLAC or 0 _EVDEF/1 PLAC
+ $pt = preg_match_all('/^[2-9] PLAC (.+)/m', $gedrec, $match, PREG_SET_ORDER);
+ for ($i = 0; $i < $pt; $i++) {
+ $place = trim($match[$i][1]);
+ $lowplace = I18N::strtolower($place);
+ //-- if we have already visited this place for this person then we don't need to again
+ if (isset($personplace[$lowplace])) {
+ continue;
+ }
+ $personplace[$lowplace] = 1;
+ $places = explode(',', $place);
+ //-- reverse the array to start at the highest level
+ $secalp = array_reverse($places);
+ $parent_id = 0;
+ $search = true;
- foreach ($secalp as $place) {
- $place = trim($place);
- $key = strtolower(mb_substr($place, 0, 150) . '_' . $parent_id);
- //-- if this place has already been added then we don't need to add it again
- if (isset($placecache[$key])) {
- $parent_id = $placecache[$key];
- if (!isset($personplace[$key])) {
- $personplace[$key] = 1;
- // Use INSERT IGNORE as a (temporary) fix for https://bugs.launchpad.net/webtrees/+bug/582226
- // It ignores places that utf8_unicode_ci consider to be the same (i.e. accents).
- // For example Québec and Quebec
- // We need a better solution that attaches multiple names to single places
- Database::prepare(
- "INSERT IGNORE INTO `##placelinks` (pl_p_id, pl_gid, pl_file) VALUES (?, ?, ?)"
- )->execute([
- $parent_id, $gid, $ged_id,
- ]);
- }
- continue;
- }
+ foreach ($secalp as $place) {
+ $place = trim($place);
+ $key = strtolower(mb_substr($place, 0, 150) . '_' . $parent_id);
+ //-- if this place has already been added then we don't need to add it again
+ if (isset($placecache[$key])) {
+ $parent_id = $placecache[$key];
+ if (!isset($personplace[$key])) {
+ $personplace[$key] = 1;
+ // Use INSERT IGNORE as a (temporary) fix for https://bugs.launchpad.net/webtrees/+bug/582226
+ // It ignores places that utf8_unicode_ci consider to be the same (i.e. accents).
+ // For example Québec and Quebec
+ // We need a better solution that attaches multiple names to single places
+ Database::prepare(
+ "INSERT IGNORE INTO `##placelinks` (pl_p_id, pl_gid, pl_file) VALUES (?, ?, ?)"
+ )->execute([
+ $parent_id,
+ $gid,
+ $ged_id,
+ ]);
+ }
+ continue;
+ }
- //-- only search the database while we are finding places in it
- if ($search) {
- //-- check if this place and level has already been added
- $tmp = Database::prepare(
- "SELECT p_id FROM `##places` WHERE p_file = ? AND p_parent_id = ? AND p_place = LEFT(?, 150)"
- )->execute([
- $ged_id, $parent_id, $place,
- ])->fetchOne();
- if ($tmp) {
- $p_id = $tmp;
- } else {
- $search = false;
- }
- }
+ //-- only search the database while we are finding places in it
+ if ($search) {
+ //-- check if this place and level has already been added
+ $tmp = Database::prepare(
+ "SELECT p_id FROM `##places` WHERE p_file = ? AND p_parent_id = ? AND p_place = LEFT(?, 150)"
+ )->execute([
+ $ged_id,
+ $parent_id,
+ $place,
+ ])->fetchOne();
+ if ($tmp) {
+ $p_id = $tmp;
+ } else {
+ $search = false;
+ }
+ }
- //-- if we are not searching then we have to insert the place into the db
- if (!$search) {
- $std_soundex = Soundex::russell($place);
- $dm_soundex = Soundex::daitchMokotoff($place);
- Database::prepare(
- "INSERT INTO `##places` (p_place, p_parent_id, p_file, p_std_soundex, p_dm_soundex) VALUES (LEFT(?, 150), ?, ?, ?, ?)"
- )->execute([
- $place, $parent_id, $ged_id, $std_soundex, $dm_soundex,
- ]);
- $p_id = Database::getInstance()->lastInsertId();
- }
+ //-- if we are not searching then we have to insert the place into the db
+ if (!$search) {
+ $std_soundex = Soundex::russell($place);
+ $dm_soundex = Soundex::daitchMokotoff($place);
+ Database::prepare(
+ "INSERT INTO `##places` (p_place, p_parent_id, p_file, p_std_soundex, p_dm_soundex) VALUES (LEFT(?, 150), ?, ?, ?, ?)"
+ )->execute([
+ $place,
+ $parent_id,
+ $ged_id,
+ $std_soundex,
+ $dm_soundex,
+ ]);
+ $p_id = Database::getInstance()->lastInsertId();
+ }
- Database::prepare(
- "INSERT IGNORE INTO `##placelinks` (pl_p_id, pl_gid, pl_file) VALUES (?, ?, ?)"
- )->execute([
- $p_id, $gid, $ged_id,
- ]);
- //-- increment the level and assign the parent id for the next place level
- $parent_id = $p_id;
- $placecache[$key] = $p_id;
- $personplace[$key] = 1;
- }
- }
- }
+ Database::prepare(
+ "INSERT IGNORE INTO `##placelinks` (pl_p_id, pl_gid, pl_file) VALUES (?, ?, ?)"
+ )->execute([
+ $p_id,
+ $gid,
+ $ged_id,
+ ]);
+ //-- increment the level and assign the parent id for the next place level
+ $parent_id = $p_id;
+ $placecache[$key] = $p_id;
+ $personplace[$key] = 1;
+ }
+ }
+ }
- /**
- * Extract all the dates from the given record and insert them into the database.
- *
- * @param string $xref
- * @param int $ged_id
- * @param string $gedrec
- */
- public static function updateDates($xref, $ged_id, $gedrec) {
- if (strpos($gedrec, '2 DATE ') && preg_match_all("/\n1 (\w+).*(?:\n[2-9].*)*(?:\n2 DATE (.+))(?:\n[2-9].*)*/", $gedrec, $matches, PREG_SET_ORDER)) {
- foreach ($matches as $match) {
- $fact = $match[1];
- if (($fact == 'FACT' || $fact == 'EVEN') && preg_match("/\n2 TYPE ([A-Z]{3,5})/", $match[0], $tmatch)) {
- $fact = $tmatch[1];
- }
- $date = new Date($match[2]);
- Database::prepare(
- "INSERT INTO `##dates` (d_day,d_month,d_mon,d_year,d_julianday1,d_julianday2,d_fact,d_gid,d_file,d_type) VALUES (?,?,?,?,?,?,?,?,?,?)"
- )->execute([
- $date->minimumDate()->d,
- $date->minimumDate()->format('%O'),
- $date->minimumDate()->m,
- $date->minimumDate()->y,
- $date->minimumDate()->minJD,
- $date->minimumDate()->maxJD,
- $fact,
- $xref,
- $ged_id,
- $date->minimumDate()->format('%@'),
- ]);
- if ($date->minimumDate() !== $date->maximumDate()) {
- Database::prepare(
- "INSERT INTO `##dates` (d_day,d_month,d_mon,d_year,d_julianday1,d_julianday2,d_fact,d_gid,d_file,d_type) VALUES (?,?,?,?,?,?,?,?,?,?)"
- )->execute([
- $date->maximumDate()->d,
- $date->maximumDate()->format('%O'),
- $date->maximumDate()->m,
- $date->maximumDate()->y,
- $date->maximumDate()->minJD,
- $date->maximumDate()->maxJD,
- $fact,
- $xref,
- $ged_id,
- $date->maximumDate()->format('%@'),
- ]);
- }
- }
- }
- }
+ /**
+ * Extract all the dates from the given record and insert them into the database.
+ *
+ * @param string $xref
+ * @param int $ged_id
+ * @param string $gedrec
+ */
+ public static function updateDates($xref, $ged_id, $gedrec)
+ {
+ if (strpos($gedrec, '2 DATE ') && preg_match_all("/\n1 (\w+).*(?:\n[2-9].*)*(?:\n2 DATE (.+))(?:\n[2-9].*)*/", $gedrec, $matches, PREG_SET_ORDER)) {
+ foreach ($matches as $match) {
+ $fact = $match[1];
+ if (($fact == 'FACT' || $fact == 'EVEN') && preg_match("/\n2 TYPE ([A-Z]{3,5})/", $match[0], $tmatch)) {
+ $fact = $tmatch[1];
+ }
+ $date = new Date($match[2]);
+ Database::prepare(
+ "INSERT INTO `##dates` (d_day,d_month,d_mon,d_year,d_julianday1,d_julianday2,d_fact,d_gid,d_file,d_type) VALUES (?,?,?,?,?,?,?,?,?,?)"
+ )->execute([
+ $date->minimumDate()->d,
+ $date->minimumDate()->format('%O'),
+ $date->minimumDate()->m,
+ $date->minimumDate()->y,
+ $date->minimumDate()->minJD,
+ $date->minimumDate()->maxJD,
+ $fact,
+ $xref,
+ $ged_id,
+ $date->minimumDate()->format('%@'),
+ ]);
+ if ($date->minimumDate() !== $date->maximumDate()) {
+ Database::prepare(
+ "INSERT INTO `##dates` (d_day,d_month,d_mon,d_year,d_julianday1,d_julianday2,d_fact,d_gid,d_file,d_type) VALUES (?,?,?,?,?,?,?,?,?,?)"
+ )->execute([
+ $date->maximumDate()->d,
+ $date->maximumDate()->format('%O'),
+ $date->maximumDate()->m,
+ $date->maximumDate()->y,
+ $date->maximumDate()->minJD,
+ $date->maximumDate()->maxJD,
+ $fact,
+ $xref,
+ $ged_id,
+ $date->maximumDate()->format('%@'),
+ ]);
+ }
+ }
+ }
+ }
- /**
- * Extract all the links from the given record and insert them into the database
- *
- * @param string $xref
- * @param int $ged_id
- * @param string $gedrec
- */
- public static function updateLinks($xref, $ged_id, $gedrec) {
- if (preg_match_all('/^\d+ (' . WT_REGEX_TAG . ') @(' . WT_REGEX_XREF . ')@/m', $gedrec, $matches, PREG_SET_ORDER)) {
- $data = [];
- foreach ($matches as $match) {
- // Include each link once only.
- if (!in_array($match[1] . $match[2], $data)) {
- $data[] = $match[1] . $match[2];
- // Ignore any errors, which may be caused by "duplicates" that differ on case/collation, e.g. "S1" and "s1"
- try {
- Database::prepare(
- "INSERT INTO `##link` (l_from, l_to, l_type, l_file) VALUES (?, ?, ?, ?)"
- )->execute([
- $xref, $match[2], $match[1], $ged_id,
- ]);
- } catch (PDOException $ex) {
- DebugBar::addThrowable($ex);
+ /**
+ * Extract all the links from the given record and insert them into the database
+ *
+ * @param string $xref
+ * @param int $ged_id
+ * @param string $gedrec
+ */
+ public static function updateLinks($xref, $ged_id, $gedrec)
+ {
+ if (preg_match_all('/^\d+ (' . WT_REGEX_TAG . ') @(' . WT_REGEX_XREF . ')@/m', $gedrec, $matches, PREG_SET_ORDER)) {
+ $data = [];
+ foreach ($matches as $match) {
+ // Include each link once only.
+ if (!in_array($match[1] . $match[2], $data)) {
+ $data[] = $match[1] . $match[2];
+ // Ignore any errors, which may be caused by "duplicates" that differ on case/collation, e.g. "S1" and "s1"
+ try {
+ Database::prepare(
+ "INSERT INTO `##link` (l_from, l_to, l_type, l_file) VALUES (?, ?, ?, ?)"
+ )->execute([
+ $xref,
+ $match[2],
+ $match[1],
+ $ged_id,
+ ]);
+ } catch (PDOException $ex) {
+ DebugBar::addThrowable($ex);
- // We could display a warning here....
- }
- }
- }
- }
- }
+ // We could display a warning here....
+ }
+ }
+ }
+ }
+ }
- /**
- * Extract all the names from the given record and insert them into the database.
- *
- * @param string $xref
- * @param int $ged_id
- * @param GedcomRecord $record
- */
- public static function updateNames($xref, $ged_id, GedcomRecord $record) {
- foreach ($record->getAllNames() as $n => $name) {
- if ($record instanceof Individual) {
- if ($name['givn'] === '@P.N.') {
- $soundex_givn_std = null;
- $soundex_givn_dm = null;
- } else {
- $soundex_givn_std = Soundex::russell($name['givn']);
- $soundex_givn_dm = Soundex::daitchMokotoff($name['givn']);
- }
- if ($name['surn'] === '@N.N.') {
- $soundex_surn_std = null;
- $soundex_surn_dm = null;
- } else {
- $soundex_surn_std = Soundex::russell($name['surname']);
- $soundex_surn_dm = Soundex::daitchMokotoff($name['surname']);
- }
- Database::prepare(
- "INSERT INTO `##name` (n_file,n_id,n_num,n_type,n_sort,n_full,n_surname,n_surn,n_givn,n_soundex_givn_std,n_soundex_surn_std,n_soundex_givn_dm,n_soundex_surn_dm) VALUES (?, ?, ?, ?, LEFT(?, 255), LEFT(?, 255), LEFT(?, 255), LEFT(?, 255), ?, ?, ?, ?, ?)"
- )->execute([
- $ged_id, $xref, $n, $name['type'], $name['sort'], $name['fullNN'], $name['surname'], $name['surn'], $name['givn'], $soundex_givn_std, $soundex_surn_std, $soundex_givn_dm, $soundex_surn_dm,
- ]);
- } else {
- Database::prepare(
- "INSERT INTO `##name` (n_file,n_id,n_num,n_type,n_sort,n_full) VALUES (?, ?, ?, ?, LEFT(?, 255), LEFT(?, 255))"
- )->execute([
- $ged_id, $xref, $n, $name['type'], $name['sort'], $name['fullNN'],
- ]);
- }
- }
- }
+ /**
+ * Extract all the names from the given record and insert them into the database.
+ *
+ * @param string $xref
+ * @param int $ged_id
+ * @param GedcomRecord $record
+ */
+ public static function updateNames($xref, $ged_id, GedcomRecord $record)
+ {
+ foreach ($record->getAllNames() as $n => $name) {
+ if ($record instanceof Individual) {
+ if ($name['givn'] === '@P.N.') {
+ $soundex_givn_std = null;
+ $soundex_givn_dm = null;
+ } else {
+ $soundex_givn_std = Soundex::russell($name['givn']);
+ $soundex_givn_dm = Soundex::daitchMokotoff($name['givn']);
+ }
+ if ($name['surn'] === '@N.N.') {
+ $soundex_surn_std = null;
+ $soundex_surn_dm = null;
+ } else {
+ $soundex_surn_std = Soundex::russell($name['surname']);
+ $soundex_surn_dm = Soundex::daitchMokotoff($name['surname']);
+ }
+ Database::prepare(
+ "INSERT INTO `##name` (n_file,n_id,n_num,n_type,n_sort,n_full,n_surname,n_surn,n_givn,n_soundex_givn_std,n_soundex_surn_std,n_soundex_givn_dm,n_soundex_surn_dm) VALUES (?, ?, ?, ?, LEFT(?, 255), LEFT(?, 255), LEFT(?, 255), LEFT(?, 255), ?, ?, ?, ?, ?)"
+ )->execute([
+ $ged_id,
+ $xref,
+ $n,
+ $name['type'],
+ $name['sort'],
+ $name['fullNN'],
+ $name['surname'],
+ $name['surn'],
+ $name['givn'],
+ $soundex_givn_std,
+ $soundex_surn_std,
+ $soundex_givn_dm,
+ $soundex_surn_dm,
+ ]);
+ } else {
+ Database::prepare(
+ "INSERT INTO `##name` (n_file,n_id,n_num,n_type,n_sort,n_full) VALUES (?, ?, ?, ?, LEFT(?, 255), LEFT(?, 255))"
+ )->execute([
+ $ged_id,
+ $xref,
+ $n,
+ $name['type'],
+ $name['sort'],
+ $name['fullNN'],
+ ]);
+ }
+ }
+ }
- /**
- * Extract inline media data, and convert to media objects.
- *
- * @param Tree $tree
- * @param string $gedrec
- *
- * @return string
- */
- public static function convertInlineMedia(Tree $tree, $gedrec) {
- while (preg_match('/\n1 OBJE(?:\n[2-9].+)+/', $gedrec, $match)) {
- $gedrec = str_replace($match[0], self::createMediaObject(1, $match[0], $tree), $gedrec);
- }
- while (preg_match('/\n2 OBJE(?:\n[3-9].+)+/', $gedrec, $match)) {
- $gedrec = str_replace($match[0], self::createMediaObject(2, $match[0], $tree), $gedrec);
- }
- while (preg_match('/\n3 OBJE(?:\n[4-9].+)+/', $gedrec, $match)) {
- $gedrec = str_replace($match[0], self::createMediaObject(3, $match[0], $tree), $gedrec);
- }
+ /**
+ * Extract inline media data, and convert to media objects.
+ *
+ * @param Tree $tree
+ * @param string $gedrec
+ *
+ * @return string
+ */
+ public static function convertInlineMedia(Tree $tree, $gedrec)
+ {
+ while (preg_match('/\n1 OBJE(?:\n[2-9].+)+/', $gedrec, $match)) {
+ $gedrec = str_replace($match[0], self::createMediaObject(1, $match[0], $tree), $gedrec);
+ }
+ while (preg_match('/\n2 OBJE(?:\n[3-9].+)+/', $gedrec, $match)) {
+ $gedrec = str_replace($match[0], self::createMediaObject(2, $match[0], $tree), $gedrec);
+ }
+ while (preg_match('/\n3 OBJE(?:\n[4-9].+)+/', $gedrec, $match)) {
+ $gedrec = str_replace($match[0], self::createMediaObject(3, $match[0], $tree), $gedrec);
+ }
- return $gedrec;
- }
+ return $gedrec;
+ }
- /**
- * Create a new media object, from inline media data.
- *
- * @param int $level
- * @param string $gedrec
- * @param Tree $tree
- *
- * @return string
- */
- public static function createMediaObject($level, $gedrec, Tree $tree) {
- if (preg_match('/\n\d FILE (.+)/', $gedrec, $file_match)) {
- $file = $file_match[1];
- } else {
- $file = '';
- }
+ /**
+ * Create a new media object, from inline media data.
+ *
+ * @param int $level
+ * @param string $gedrec
+ * @param Tree $tree
+ *
+ * @return string
+ */
+ public static function createMediaObject($level, $gedrec, Tree $tree)
+ {
+ if (preg_match('/\n\d FILE (.+)/', $gedrec, $file_match)) {
+ $file = $file_match[1];
+ } else {
+ $file = '';
+ }
- if (preg_match('/\n\d TITL (.+)/', $gedrec, $file_match)) {
- $titl = $file_match[1];
- } else {
- $titl = '';
- }
+ if (preg_match('/\n\d TITL (.+)/', $gedrec, $file_match)) {
+ $titl = $file_match[1];
+ } else {
+ $titl = '';
+ }
- // Have we already created a media object with the same title/filename?
- $xref = Database::prepare(
- "SELECT m_id FROM `##media_file`" .
- " WHERE multimedia_file_refn = :filename AND descriptive_title = :title AND m_file = :tree_id"
- )->execute([
- 'filename' => $file,
- 'title' => $titl,
- 'tree_id' => $tree->getTreeId(),
- ])->fetchOne();
+ // Have we already created a media object with the same title/filename?
+ $xref = Database::prepare(
+ "SELECT m_id FROM `##media_file`" .
+ " WHERE multimedia_file_refn = :filename AND descriptive_title = :title AND m_file = :tree_id"
+ )->execute([
+ 'filename' => $file,
+ 'title' => $titl,
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchOne();
- if (!$xref) {
- $xref = $tree->getNewXref();
- // renumber the lines
- $gedrec = preg_replace_callback('/\n(\d+)/', function ($m) use ($level) {
- return "\n" . ($m[1] - $level);
- }, $gedrec);
- // convert to an object
- $gedrec = str_replace("\n0 OBJE\n", '0 @' . $xref . "@ OBJE\n", $gedrec);
- // Fix Legacy GEDCOMS
- $gedrec = preg_replace('/\n1 FORM (.+)\n1 FILE (.+)\n1 TITL (.+)/', "\n1 FILE $2\n2 FORM $1\n2 TITL $3", $gedrec);
- // Fix FTB GEDCOMS
- $gedrec = preg_replace('/\n1 FORM (.+)\n1 TITL (.+)\n1 FILE (.+)/', "\n1 FILE $3\n2 FORM $1\n2 TITL $2", $gedrec);
- // Create new record
- $record = new Media($xref, $gedrec, null, $tree);
+ if (!$xref) {
+ $xref = $tree->getNewXref();
+ // renumber the lines
+ $gedrec = preg_replace_callback('/\n(\d+)/', function ($m) use ($level) {
+ return "\n" . ($m[1] - $level);
+ }, $gedrec);
+ // convert to an object
+ $gedrec = str_replace("\n0 OBJE\n", '0 @' . $xref . "@ OBJE\n", $gedrec);
+ // Fix Legacy GEDCOMS
+ $gedrec = preg_replace('/\n1 FORM (.+)\n1 FILE (.+)\n1 TITL (.+)/', "\n1 FILE $2\n2 FORM $1\n2 TITL $3", $gedrec);
+ // Fix FTB GEDCOMS
+ $gedrec = preg_replace('/\n1 FORM (.+)\n1 TITL (.+)\n1 FILE (.+)/', "\n1 FILE $3\n2 FORM $1\n2 TITL $2", $gedrec);
+ // Create new record
+ $record = new Media($xref, $gedrec, null, $tree);
- Database::prepare(
- "INSERT INTO `##media` (m_id, m_file, m_gedcom) VALUES (:m_id, :m_file, :m_gedcom)"
- )->execute([
- 'm_id' => $xref,
- 'm_file' => $tree->getTreeId(),
- 'm_gedcom' => $gedrec,
- ]);
+ Database::prepare(
+ "INSERT INTO `##media` (m_id, m_file, m_gedcom) VALUES (:m_id, :m_file, :m_gedcom)"
+ )->execute([
+ 'm_id' => $xref,
+ 'm_file' => $tree->getTreeId(),
+ 'm_gedcom' => $gedrec,
+ ]);
- foreach ($record->mediaFiles() as $media_file) {
- Database::prepare(
- "INSERT INTO `##media_file` (m_id, m_file, multimedia_file_refn, multimedia_format, source_media_type, descriptive_title) VALUES (:m_id, :m_file, LEFT(:multimedia_file_refn, 512), LEFT(:multimedia_format, 4), LEFT(:source_media_type, 15), LEFT(:descriptive_title, 248))"
- )->execute([
- 'm_id' => $xref,
- 'm_file' => $tree->getTreeId(),
- 'multimedia_file_refn' => $media_file->filename(),
- 'multimedia_format' => $media_file->format(),
- 'source_media_type' => $media_file->type(),
- 'descriptive_title' => $media_file->title(),
- ]);
- }
+ foreach ($record->mediaFiles() as $media_file) {
+ Database::prepare(
+ "INSERT INTO `##media_file` (m_id, m_file, multimedia_file_refn, multimedia_format, source_media_type, descriptive_title) VALUES (:m_id, :m_file, LEFT(:multimedia_file_refn, 512), LEFT(:multimedia_format, 4), LEFT(:source_media_type, 15), LEFT(:descriptive_title, 248))"
+ )->execute([
+ 'm_id' => $xref,
+ 'm_file' => $tree->getTreeId(),
+ 'multimedia_file_refn' => $media_file->filename(),
+ 'multimedia_format' => $media_file->format(),
+ 'source_media_type' => $media_file->type(),
+ 'descriptive_title' => $media_file->title(),
+ ]);
+ }
- }
+ }
- return "\n" . $level . ' OBJE @' . $xref . '@';
- }
+ return "\n" . $level . ' OBJE @' . $xref . '@';
+ }
- /**
- * Accept all pending changes for a specified record.
- *
- * @param string $xref
- * @param Tree $tree
- */
- public static function acceptAllChanges($xref, Tree $tree) {
- $changes = Database::prepare(
- "SELECT change_id, gedcom_name, old_gedcom, new_gedcom" .
- " FROM `##change` c" .
- " JOIN `##gedcom` g USING (gedcom_id)" .
- " WHERE c.status='pending' AND xref = :xref AND gedcom_id = :tree_id" .
- " ORDER BY change_id"
- )->execute([
- 'xref' => $xref,
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
- foreach ($changes as $change) {
- if (empty($change->new_gedcom)) {
- // delete
- self::updateRecord($change->old_gedcom, $tree, true);
- } else {
- // add/update
- self::updateRecord($change->new_gedcom, $tree, false);
- }
- Database::prepare(
- "UPDATE `##change` SET status='accepted' WHERE status='pending' AND xref=? AND gedcom_id=?"
- )->execute([
- $xref,
- $tree->getTreeId(),
- ]);
- Log::addEditLog("Accepted change {$change->change_id} for {$xref} / {$change->gedcom_name} into database", $tree);
- }
- }
+ /**
+ * Accept all pending changes for a specified record.
+ *
+ * @param string $xref
+ * @param Tree $tree
+ */
+ public static function acceptAllChanges($xref, Tree $tree)
+ {
+ $changes = Database::prepare(
+ "SELECT change_id, gedcom_name, old_gedcom, new_gedcom" .
+ " FROM `##change` c" .
+ " JOIN `##gedcom` g USING (gedcom_id)" .
+ " WHERE c.status='pending' AND xref = :xref AND gedcom_id = :tree_id" .
+ " ORDER BY change_id"
+ )->execute([
+ 'xref' => $xref,
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
+ foreach ($changes as $change) {
+ if (empty($change->new_gedcom)) {
+ // delete
+ self::updateRecord($change->old_gedcom, $tree, true);
+ } else {
+ // add/update
+ self::updateRecord($change->new_gedcom, $tree, false);
+ }
+ Database::prepare(
+ "UPDATE `##change` SET status='accepted' WHERE status='pending' AND xref=? AND gedcom_id=?"
+ )->execute([
+ $xref,
+ $tree->getTreeId(),
+ ]);
+ Log::addEditLog("Accepted change {$change->change_id} for {$xref} / {$change->gedcom_name} into database", $tree);
+ }
+ }
- /**
- * Reject all pending changes for a specified record.
- *
- * @param GedcomRecord $record
- */
- public static function rejectAllChanges(GedcomRecord $record) {
- Database::prepare(
- "UPDATE `##change`" .
- " SET status = 'rejected'" .
- " WHERE status = 'pending' AND xref = :xref AND gedcom_id = :tree_id"
- )->execute([
- 'xref' => $record->getXref(),
- 'tree_id' => $record->getTree()->getTreeId(),
- ]);
- }
+ /**
+ * Reject all pending changes for a specified record.
+ *
+ * @param GedcomRecord $record
+ */
+ public static function rejectAllChanges(GedcomRecord $record)
+ {
+ Database::prepare(
+ "UPDATE `##change`" .
+ " SET status = 'rejected'" .
+ " WHERE status = 'pending' AND xref = :xref AND gedcom_id = :tree_id"
+ )->execute([
+ 'xref' => $record->getXref(),
+ 'tree_id' => $record->getTree()->getTreeId(),
+ ]);
+ }
- /**
- * update a record in the database
- *
- * @param string $gedrec
- * @param Tree $tree
- * @param bool $delete
- */
- public static function updateRecord($gedrec, Tree $tree, bool $delete) {
- if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedrec, $match)) {
- list(, $gid, $type) = $match;
- } elseif (preg_match('/^0 (HEAD)(?:\n|$)/', $gedrec, $match)) {
- // The HEAD record has no XREF. Any others?
- $gid = $match[1];
- $type = $match[1];
- } else {
- echo 'ERROR: Invalid gedcom record.';
+ /**
+ * update a record in the database
+ *
+ * @param string $gedrec
+ * @param Tree $tree
+ * @param bool $delete
+ */
+ public static function updateRecord($gedrec, Tree $tree, bool $delete)
+ {
+ if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedrec, $match)) {
+ list(, $gid, $type) = $match;
+ } elseif (preg_match('/^0 (HEAD)(?:\n|$)/', $gedrec, $match)) {
+ // The HEAD record has no XREF. Any others?
+ $gid = $match[1];
+ $type = $match[1];
+ } else {
+ echo 'ERROR: Invalid gedcom record.';
- return;
- }
+ return;
+ }
- // TODO deleting unlinked places can be done more efficiently in a single query
- $placeids =
- Database::prepare(
- "SELECT pl_p_id FROM `##placelinks` WHERE pl_gid=? AND pl_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ])->fetchOneColumn();
+ // TODO deleting unlinked places can be done more efficiently in a single query
+ $placeids =
+ Database::prepare(
+ "SELECT pl_p_id FROM `##placelinks` WHERE pl_gid=? AND pl_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ])->fetchOneColumn();
- Database::prepare(
- "DELETE FROM `##placelinks` WHERE pl_gid = ? AND pl_file = ?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
+ Database::prepare(
+ "DELETE FROM `##placelinks` WHERE pl_gid = ? AND pl_file = ?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
- Database::prepare(
- "DELETE FROM `##dates` WHERE d_gid =? AND d_file = ?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
+ Database::prepare(
+ "DELETE FROM `##dates` WHERE d_gid =? AND d_file = ?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
- //-- delete any unlinked places
- foreach ($placeids as $p_id) {
- $num = Database::prepare(
- "SELECT count(pl_p_id) FROM `##placelinks` WHERE pl_p_id=? AND pl_file=?"
- )->execute([
- $p_id,
- $tree->getTreeId(),
- ])->fetchOne();
+ //-- delete any unlinked places
+ foreach ($placeids as $p_id) {
+ $num = Database::prepare(
+ "SELECT count(pl_p_id) FROM `##placelinks` WHERE pl_p_id=? AND pl_file=?"
+ )->execute([
+ $p_id,
+ $tree->getTreeId(),
+ ])->fetchOne();
- if ($num == 0) {
- Database::prepare(
- "DELETE FROM `##places` WHERE p_id=? AND p_file=?"
- )->execute([
- $p_id,
- $tree->getTreeId(),
- ]);
- }
- }
+ if ($num == 0) {
+ Database::prepare(
+ "DELETE FROM `##places` WHERE p_id=? AND p_file=?"
+ )->execute([
+ $p_id,
+ $tree->getTreeId(),
+ ]);
+ }
+ }
- Database::prepare(
- "DELETE FROM `##name` WHERE n_id=? AND n_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
+ Database::prepare(
+ "DELETE FROM `##name` WHERE n_id=? AND n_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
- Database::prepare(
- "DELETE FROM `##link` WHERE l_from=? AND l_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
+ Database::prepare(
+ "DELETE FROM `##link` WHERE l_from=? AND l_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
- switch ($type) {
- case 'INDI':
- Database::prepare(
- "DELETE FROM `##individuals` WHERE i_id=? AND i_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
- break;
+ switch ($type) {
+ case 'INDI':
+ Database::prepare(
+ "DELETE FROM `##individuals` WHERE i_id=? AND i_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
+ break;
- case 'FAM':
- Database::prepare(
- "DELETE FROM `##families` WHERE f_id=? AND f_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
- break;
+ case 'FAM':
+ Database::prepare(
+ "DELETE FROM `##families` WHERE f_id=? AND f_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
+ break;
- case 'SOUR':
- Database::prepare(
- "DELETE FROM `##sources` WHERE s_id=? AND s_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
- break;
+ case 'SOUR':
+ Database::prepare(
+ "DELETE FROM `##sources` WHERE s_id=? AND s_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
+ break;
- case 'OBJE':
- Database::prepare(
- "DELETE FROM `##media` WHERE m_id=? AND m_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
+ case 'OBJE':
+ Database::prepare(
+ "DELETE FROM `##media` WHERE m_id=? AND m_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
- Database::prepare(
- "DELETE FROM `##media_file` WHERE m_id=? AND m_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
- break;
+ Database::prepare(
+ "DELETE FROM `##media_file` WHERE m_id=? AND m_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
+ break;
- default:
- Database::prepare(
- "DELETE FROM `##other` WHERE o_id=? AND o_file=?"
- )->execute([
- $gid,
- $tree->getTreeId(),
- ]);
- break;
- }
+ default:
+ Database::prepare(
+ "DELETE FROM `##other` WHERE o_id=? AND o_file=?"
+ )->execute([
+ $gid,
+ $tree->getTreeId(),
+ ]);
+ break;
+ }
- if (!$delete) {
- self::importRecord($gedrec, $tree, true);
- }
- }
+ if (!$delete) {
+ self::importRecord($gedrec, $tree, true);
+ }
+ }
}