summaryrefslogtreecommitdiff
path: root/app/Functions/FunctionsExport.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Functions/FunctionsExport.php')
-rw-r--r--app/Functions/FunctionsExport.php499
1 files changed, 255 insertions, 244 deletions
diff --git a/app/Functions/FunctionsExport.php b/app/Functions/FunctionsExport.php
index afc13eb7cd..23a424055d 100644
--- a/app/Functions/FunctionsExport.php
+++ b/app/Functions/FunctionsExport.php
@@ -31,269 +31,280 @@ use Fisharebest\Webtrees\Tree;
/**
* Class FunctionsExport - common functions
*/
-class FunctionsExport {
- /**
- * Tidy up a gedcom record on export, for compatibility/portability.
- *
- * @param string $rec
- *
- * @return string
- */
- public static function reformatRecord($rec) {
- $newrec = '';
- foreach (preg_split('/[\r\n]+/', $rec, -1, PREG_SPLIT_NO_EMPTY) as $line) {
- // Split long lines
- // The total length of a GEDCOM line, including level number, cross-reference number,
- // tag, value, delimiters, and terminator, must not exceed 255 (wide) characters.
- if (mb_strlen($line) > Gedcom::LINE_LENGTH) {
- list($level, $tag) = explode(' ', $line, 3);
- if ($tag != 'CONT' && $tag != 'CONC') {
- $level++;
- }
- do {
- // Split after $pos chars
- $pos = Gedcom::LINE_LENGTH;
- // Split on a non-space (standard gedcom behaviour)
- while ($pos && mb_substr($line, $pos - 1, 1) == ' ') {
- --$pos;
- }
- if ($pos == strpos($line, ' ', 3)) {
- // No non-spaces in the data! Can’t split it :-(
- break;
- }
- $newrec .= mb_substr($line, 0, $pos) . Gedcom::EOL;
- $line = $level . ' CONC ' . mb_substr($line, $pos);
- } while (mb_strlen($line) > Gedcom::LINE_LENGTH);
- }
- $newrec .= $line . Gedcom::EOL;
- }
+class FunctionsExport
+{
+ /**
+ * Tidy up a gedcom record on export, for compatibility/portability.
+ *
+ * @param string $rec
+ *
+ * @return string
+ */
+ public static function reformatRecord($rec)
+ {
+ $newrec = '';
+ foreach (preg_split('/[\r\n]+/', $rec, -1, PREG_SPLIT_NO_EMPTY) as $line) {
+ // Split long lines
+ // The total length of a GEDCOM line, including level number, cross-reference number,
+ // tag, value, delimiters, and terminator, must not exceed 255 (wide) characters.
+ if (mb_strlen($line) > Gedcom::LINE_LENGTH) {
+ list($level, $tag) = explode(' ', $line, 3);
+ if ($tag != 'CONT' && $tag != 'CONC') {
+ $level++;
+ }
+ do {
+ // Split after $pos chars
+ $pos = Gedcom::LINE_LENGTH;
+ // Split on a non-space (standard gedcom behaviour)
+ while ($pos && mb_substr($line, $pos - 1, 1) == ' ') {
+ --$pos;
+ }
+ if ($pos == strpos($line, ' ', 3)) {
+ // No non-spaces in the data! Can’t split it :-(
+ break;
+ }
+ $newrec .= mb_substr($line, 0, $pos) . Gedcom::EOL;
+ $line = $level . ' CONC ' . mb_substr($line, $pos);
+ } while (mb_strlen($line) > Gedcom::LINE_LENGTH);
+ }
+ $newrec .= $line . Gedcom::EOL;
+ }
- return $newrec;
- }
+ return $newrec;
+ }
- /**
- * Create a header for a (newly-created or already-imported) gedcom file.
- *
- * @param Tree $tree
- *
- * @return string
- */
- public static function gedcomHeader(Tree $tree) {
- // Default values for a new header
- $HEAD = '0 HEAD';
- $SOUR = "\n1 SOUR " . WT_WEBTREES . "\n2 NAME " . WT_WEBTREES . "\n2 VERS " . WT_VERSION;
- $DEST = "\n1 DEST DISKETTE";
- $DATE = "\n1 DATE " . strtoupper(date('d M Y')) . "\n2 TIME " . date('H:i:s');
- $GEDC = "\n1 GEDC\n2 VERS 5.5.1\n2 FORM Lineage-Linked";
- $CHAR = "\n1 CHAR UTF-8";
- $FILE = "\n1 FILE " . $tree->getName();
- $LANG = '';
- $COPR = '';
- $SUBN = '';
- $SUBM = "\n1 SUBM @SUBM@\n0 @SUBM@ SUBM\n1 NAME " . Auth::user()->getUserName(); // The SUBM record is mandatory
+ /**
+ * Create a header for a (newly-created or already-imported) gedcom file.
+ *
+ * @param Tree $tree
+ *
+ * @return string
+ */
+ public static function gedcomHeader(Tree $tree)
+ {
+ // Default values for a new header
+ $HEAD = '0 HEAD';
+ $SOUR = "\n1 SOUR " . WT_WEBTREES . "\n2 NAME " . WT_WEBTREES . "\n2 VERS " . WT_VERSION;
+ $DEST = "\n1 DEST DISKETTE";
+ $DATE = "\n1 DATE " . strtoupper(date('d M Y')) . "\n2 TIME " . date('H:i:s');
+ $GEDC = "\n1 GEDC\n2 VERS 5.5.1\n2 FORM Lineage-Linked";
+ $CHAR = "\n1 CHAR UTF-8";
+ $FILE = "\n1 FILE " . $tree->getName();
+ $LANG = '';
+ $COPR = '';
+ $SUBN = '';
+ $SUBM = "\n1 SUBM @SUBM@\n0 @SUBM@ SUBM\n1 NAME " . Auth::user()->getUserName(); // The SUBM record is mandatory
- // Preserve some values from the original header
- $record = GedcomRecord::getInstance('HEAD', $tree);
- $fact = $record->getFirstFact('LANG');
- if ($fact instanceof Fact) {
- $LANG = $fact->getValue();
- }
- $fact = $record->getFirstFact('SUBN');
- if ($fact instanceof Fact) {
- $SUBN = $fact->getValue();
- }
- $fact = $record->getFirstFact('COPR');
- if ($fact instanceof Fact) {
- $COPR = $fact->getValue();
- }
- // Link to actual SUBM/SUBN records, if they exist
- $subn =
- Database::prepare("SELECT o_id FROM `##other` WHERE o_type=? AND o_file=?")
- ->execute(['SUBN', $tree->getTreeId()])
- ->fetchOne();
- if ($subn) {
- $SUBN = "\n1 SUBN @{$subn}@";
- }
- $subm =
- Database::prepare("SELECT o_id FROM `##other` WHERE o_type=? AND o_file=?")
- ->execute(['SUBM', $tree->getTreeId()])
- ->fetchOne();
- if ($subm) {
- $SUBM = "\n1 SUBM @{$subm}@";
- }
+ // Preserve some values from the original header
+ $record = GedcomRecord::getInstance('HEAD', $tree);
+ $fact = $record->getFirstFact('LANG');
+ if ($fact instanceof Fact) {
+ $LANG = $fact->getValue();
+ }
+ $fact = $record->getFirstFact('SUBN');
+ if ($fact instanceof Fact) {
+ $SUBN = $fact->getValue();
+ }
+ $fact = $record->getFirstFact('COPR');
+ if ($fact instanceof Fact) {
+ $COPR = $fact->getValue();
+ }
+ // Link to actual SUBM/SUBN records, if they exist
+ $subn =
+ Database::prepare("SELECT o_id FROM `##other` WHERE o_type=? AND o_file=?")
+ ->execute([
+ 'SUBN',
+ $tree->getTreeId(),
+ ])
+ ->fetchOne();
+ if ($subn) {
+ $SUBN = "\n1 SUBN @{$subn}@";
+ }
+ $subm =
+ Database::prepare("SELECT o_id FROM `##other` WHERE o_type=? AND o_file=?")
+ ->execute([
+ 'SUBM',
+ $tree->getTreeId(),
+ ])
+ ->fetchOne();
+ if ($subm) {
+ $SUBM = "\n1 SUBM @{$subm}@";
+ }
- return $HEAD . $SOUR . $DEST . $DATE . $GEDC . $CHAR . $FILE . $COPR . $LANG . $SUBN . $SUBM . "\n";
- }
+ return $HEAD . $SOUR . $DEST . $DATE . $GEDC . $CHAR . $FILE . $COPR . $LANG . $SUBN . $SUBM . "\n";
+ }
- /**
- * Prepend the GEDCOM_MEDIA_PATH to media filenames.
- *
- * @param string $rec
- * @param string $path
- *
- * @return string
- */
- public static function convertMediaPath($rec, $path) {
- if ($path && preg_match('/\n1 FILE (.+)/', $rec, $match)) {
- $old_file_name = $match[1];
- // Don’t modify external links
- if (strpos($old_file_name, '://') === false) {
- // Adding a windows path? Convert the slashes.
- if (strpos($path, '\\') !== false) {
- $new_file_name = preg_replace('~/+~', '\\', $old_file_name);
- } else {
- $new_file_name = $old_file_name;
- }
- // Path not present - add it.
- if (strpos($new_file_name, $path) === false) {
- $new_file_name = $path . $new_file_name;
- }
- $rec = str_replace("\n1 FILE " . $old_file_name, "\n1 FILE " . $new_file_name, $rec);
- }
- }
+ /**
+ * Prepend the GEDCOM_MEDIA_PATH to media filenames.
+ *
+ * @param string $rec
+ * @param string $path
+ *
+ * @return string
+ */
+ public static function convertMediaPath($rec, $path)
+ {
+ if ($path && preg_match('/\n1 FILE (.+)/', $rec, $match)) {
+ $old_file_name = $match[1];
+ // Don’t modify external links
+ if (strpos($old_file_name, '://') === false) {
+ // Adding a windows path? Convert the slashes.
+ if (strpos($path, '\\') !== false) {
+ $new_file_name = preg_replace('~/+~', '\\', $old_file_name);
+ } else {
+ $new_file_name = $old_file_name;
+ }
+ // Path not present - add it.
+ if (strpos($new_file_name, $path) === false) {
+ $new_file_name = $path . $new_file_name;
+ }
+ $rec = str_replace("\n1 FILE " . $old_file_name, "\n1 FILE " . $new_file_name, $rec);
+ }
+ }
- return $rec;
- }
+ return $rec;
+ }
- /**
- * Export the database in GEDCOM format
- *
- * @param Tree $tree Which tree to export
- * @param resource $gedout Handle to a writable stream
- * @param string[] $exportOptions Export options are as follows:
- * 'privatize': which Privacy rules apply? (none, visitor, user, manager)
- * 'toANSI': should the output be produced in ISO-8859-1 instead of UTF-8? (yes, no)
- * 'path': what constant should prefix all media file paths? (eg: media/ or c:\my pictures\my family
- * 'slashes': what folder separators apply to media file paths? (forward, backward)
- */
- public static function exportGedcom(Tree $tree, $gedout, $exportOptions) {
- switch ($exportOptions['privatize']) {
- case 'gedadmin':
- $access_level = Auth::PRIV_NONE;
- break;
- case 'user':
- $access_level = Auth::PRIV_USER;
- break;
- case 'visitor':
- $access_level = Auth::PRIV_PRIVATE;
- break;
- case 'none':
- $access_level = Auth::PRIV_HIDE;
- break;
- }
+ /**
+ * Export the database in GEDCOM format
+ *
+ * @param Tree $tree Which tree to export
+ * @param resource $gedout Handle to a writable stream
+ * @param string[] $exportOptions Export options are as follows:
+ * 'privatize': which Privacy rules apply? (none, visitor, user, manager)
+ * 'toANSI': should the output be produced in ISO-8859-1 instead of UTF-8? (yes, no)
+ * 'path': what constant should prefix all media file paths? (eg: media/ or c:\my pictures\my family
+ * 'slashes': what folder separators apply to media file paths? (forward, backward)
+ */
+ public static function exportGedcom(Tree $tree, $gedout, $exportOptions)
+ {
+ switch ($exportOptions['privatize']) {
+ case 'gedadmin':
+ $access_level = Auth::PRIV_NONE;
+ break;
+ case 'user':
+ $access_level = Auth::PRIV_USER;
+ break;
+ case 'visitor':
+ $access_level = Auth::PRIV_PRIVATE;
+ break;
+ case 'none':
+ $access_level = Auth::PRIV_HIDE;
+ break;
+ }
- $head = self::gedcomHeader($tree);
- if ($exportOptions['toANSI'] == 'yes') {
- $head = str_replace('UTF-8', 'ANSI', $head);
- $head = utf8_decode($head);
- }
- $head = self::reformatRecord($head);
- fwrite($gedout, $head);
+ $head = self::gedcomHeader($tree);
+ if ($exportOptions['toANSI'] == 'yes') {
+ $head = str_replace('UTF-8', 'ANSI', $head);
+ $head = utf8_decode($head);
+ }
+ $head = self::reformatRecord($head);
+ fwrite($gedout, $head);
- // Buffer the output. Lots of small fwrite() calls can be very slow when writing large gedcoms.
- $buffer = '';
+ // Buffer the output. Lots of small fwrite() calls can be very slow when writing large gedcoms.
+ $buffer = '';
- // Generate the OBJE/SOUR/REPO/NOTE records first, as their privacy calcualations involve
- // database queries, and we wish to avoid large gaps between queries due to MySQL connection timeouts.
- $tmp_gedcom = '';
- $rows = Database::prepare(
- "SELECT m_id AS xref, m_gedcom AS gedcom" .
- " FROM `##media` WHERE m_file = :tree_id ORDER BY m_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ // Generate the OBJE/SOUR/REPO/NOTE records first, as their privacy calcualations involve
+ // database queries, and we wish to avoid large gaps between queries due to MySQL connection timeouts.
+ $tmp_gedcom = '';
+ $rows = Database::prepare(
+ "SELECT m_id AS xref, m_gedcom AS gedcom" .
+ " FROM `##media` WHERE m_file = :tree_id ORDER BY m_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- foreach ($rows as $row) {
- $rec = Media::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
- $rec = self::convertMediaPath($rec, $exportOptions['path']);
- if ($exportOptions['toANSI'] === 'yes') {
- $rec = utf8_decode($rec);
- }
- $tmp_gedcom .= self::reformatRecord($rec);
- }
+ foreach ($rows as $row) {
+ $rec = Media::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
+ $rec = self::convertMediaPath($rec, $exportOptions['path']);
+ if ($exportOptions['toANSI'] === 'yes') {
+ $rec = utf8_decode($rec);
+ }
+ $tmp_gedcom .= self::reformatRecord($rec);
+ }
- $rows = Database::prepare(
- "SELECT s_id AS xref, s_file AS gedcom_id, s_gedcom AS gedcom" .
- " FROM `##sources` WHERE s_file = :tree_id ORDER BY s_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ $rows = Database::prepare(
+ "SELECT s_id AS xref, s_file AS gedcom_id, s_gedcom AS gedcom" .
+ " FROM `##sources` WHERE s_file = :tree_id ORDER BY s_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- foreach ($rows as $row) {
- $rec = Source::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
- if ($exportOptions['toANSI'] === 'yes') {
- $rec = utf8_decode($rec);
- }
- $tmp_gedcom .= self::reformatRecord($rec);
- }
+ foreach ($rows as $row) {
+ $rec = Source::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
+ if ($exportOptions['toANSI'] === 'yes') {
+ $rec = utf8_decode($rec);
+ }
+ $tmp_gedcom .= self::reformatRecord($rec);
+ }
- $rows = Database::prepare(
- "SELECT o_type AS type, o_id AS xref, o_gedcom AS gedcom" .
- " FROM `##other` WHERE o_file = :tree_id AND o_type NOT IN ('HEAD', 'TRLR') ORDER BY o_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ $rows = Database::prepare(
+ "SELECT o_type AS type, o_id AS xref, o_gedcom AS gedcom" .
+ " FROM `##other` WHERE o_file = :tree_id AND o_type NOT IN ('HEAD', 'TRLR') ORDER BY o_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- foreach ($rows as $row) {
- switch ($row->type) {
- case 'NOTE':
- $record = Note::getInstance($row->xref, $tree, $row->gedcom);
- break;
- case 'REPO':
- $record = Repository::getInstance($row->xref, $tree, $row->gedcom);
- break;
- default:
- $record = GedcomRecord::getInstance($row->xref, $tree, $row->gedcom);
- break;
- }
+ foreach ($rows as $row) {
+ switch ($row->type) {
+ case 'NOTE':
+ $record = Note::getInstance($row->xref, $tree, $row->gedcom);
+ break;
+ case 'REPO':
+ $record = Repository::getInstance($row->xref, $tree, $row->gedcom);
+ break;
+ default:
+ $record = GedcomRecord::getInstance($row->xref, $tree, $row->gedcom);
+ break;
+ }
- $rec = $record->privatizeGedcom($access_level);
- if ($exportOptions['toANSI'] === 'yes') {
- $rec = utf8_decode($rec);
- }
- $tmp_gedcom .= self::reformatRecord($rec);
- }
+ $rec = $record->privatizeGedcom($access_level);
+ if ($exportOptions['toANSI'] === 'yes') {
+ $rec = utf8_decode($rec);
+ }
+ $tmp_gedcom .= self::reformatRecord($rec);
+ }
- $rows = Database::prepare(
- "SELECT i_id AS xref, i_gedcom AS gedcom" .
- " FROM `##individuals` WHERE i_file = :tree_id ORDER BY i_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ $rows = Database::prepare(
+ "SELECT i_id AS xref, i_gedcom AS gedcom" .
+ " FROM `##individuals` WHERE i_file = :tree_id ORDER BY i_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- foreach ($rows as $row) {
- $rec = Individual::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
- if ($exportOptions['toANSI'] === 'yes') {
- $rec = utf8_decode($rec);
- }
- $buffer .= self::reformatRecord($rec);
- if (strlen($buffer) > 65536) {
- fwrite($gedout, $buffer);
- $buffer = '';
- }
- }
+ foreach ($rows as $row) {
+ $rec = Individual::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
+ if ($exportOptions['toANSI'] === 'yes') {
+ $rec = utf8_decode($rec);
+ }
+ $buffer .= self::reformatRecord($rec);
+ if (strlen($buffer) > 65536) {
+ fwrite($gedout, $buffer);
+ $buffer = '';
+ }
+ }
- $rows = Database::prepare(
- "SELECT f_id AS xref, f_gedcom AS gedcom" .
- " FROM `##families` WHERE f_file = :tree_id ORDER BY f_id"
- )->execute([
- 'tree_id' => $tree->getTreeId(),
- ])->fetchAll();
+ $rows = Database::prepare(
+ "SELECT f_id AS xref, f_gedcom AS gedcom" .
+ " FROM `##families` WHERE f_file = :tree_id ORDER BY f_id"
+ )->execute([
+ 'tree_id' => $tree->getTreeId(),
+ ])->fetchAll();
- foreach ($rows as $row) {
- $rec = Family::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
- if ($exportOptions['toANSI'] === 'yes') {
- $rec = utf8_decode($rec);
- }
- $buffer .= self::reformatRecord($rec);
- if (strlen($buffer) > 65536) {
- fwrite($gedout, $buffer);
- $buffer = '';
- }
- }
+ foreach ($rows as $row) {
+ $rec = Family::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
+ if ($exportOptions['toANSI'] === 'yes') {
+ $rec = utf8_decode($rec);
+ }
+ $buffer .= self::reformatRecord($rec);
+ if (strlen($buffer) > 65536) {
+ fwrite($gedout, $buffer);
+ $buffer = '';
+ }
+ }
- fwrite($gedout, $buffer);
- fwrite($gedout, $tmp_gedcom);
- fwrite($gedout, '0 TRLR' . Gedcom::EOL);
- }
+ fwrite($gedout, $buffer);
+ fwrite($gedout, $tmp_gedcom);
+ fwrite($gedout, '0 TRLR' . Gedcom::EOL);
+ }
}