summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--admin.php1
-rw-r--r--app/Report/ReportBase.php2560
-rw-r--r--app/Report/ReportParserBase.php14
-rw-r--r--app/Report/ReportParserGenerate.php2625
-rw-r--r--app/Report/ReportParserSetup.php2
-rw-r--r--reportengine.php247
6 files changed, 2635 insertions, 2814 deletions
diff --git a/admin.php b/admin.php
index c416805a10..52c3ffeba5 100644
--- a/admin.php
+++ b/admin.php
@@ -356,6 +356,7 @@ $old_files = array(
// Removed in 1.7.0
WT_ROOT . 'admin_site_other.php',
WT_ROOT . 'includes/hitcount.php',
+ WT_ROOT . 'includes/reportheader.php',
WT_ROOT . 'js',
WT_ROOT . 'language/en_GB.mo', // Replaced with en-GB.mo
WT_ROOT . 'language/en_US.mo', // Replaced with en-US.mo
diff --git a/app/Report/ReportBase.php b/app/Report/ReportBase.php
index 71fb78df33..11d3678547 100644
--- a/app/Report/ReportBase.php
+++ b/app/Report/ReportBase.php
@@ -485,2566 +485,6 @@ class ReportBase {
}
/**
- *XML start element handler
- *
- * This function is called whenever a starting element is reached
- * The element handler will be called if found, otherwise it must be HTML
- *
- * @param resource $parser the resource handler for the XML parser
- * @param string $name the name of the XML element parsed
- * @param array $attrs an array of key value pairs for the attributes
- */
-function startElement($parser, $name, $attrs) {
- global $elementHandler, $processIfs, $processGedcoms, $processRepeats, $vars;
- global $processFootnote;
-
- $newattrs = array();
-
- foreach ($attrs as $key => $value) {
- if (preg_match("/^\\$(\w+)$/", $value, $match)) {
- if ((isset($vars[$match[1]]['id'])) && (!isset($vars[$match[1]]['gedcom']))) {
- $value = $vars[$match[1]]['id'];
- }
- }
- $newattrs[$key] = $value;
- }
- $attrs = $newattrs;
- if (($processFootnote) && ($processIfs == 0 || $name == "if") && ($processGedcoms == 0 || $name == "Gedcom") && ($processRepeats == 0 || $name == "Facts" || $name == "RepeatTag")) {
- if (isset($elementHandler[$name]['start'])) {
- if ($elementHandler[$name]['start'] != "") {
- call_user_func($elementHandler[$name]['start'], $attrs);
- }
- } elseif (!isset($elementHandler[$name]['end'])) {
- htmlStartHandler($name, $attrs);
- }
- }
-}
-
-/**
- * XML end element handler
- *
- * This function is called whenever an ending element is reached
- * The element handler will be called if found, otherwise it must be HTML
- *
- * @param resource $parser the resource handler for the XML parser
- * @param string $name the name of the XML element parsed
- */
-function endElement($parser, $name) {
- global $elementHandler, $processIfs, $processGedcoms, $processRepeats;
- global $processFootnote;
-
- if (($processFootnote || $name == "Footnote") && ($processIfs == 0 || $name == "if") && ($processGedcoms == 0 || $name == "Gedcom") && ($processRepeats == 0 || $name == "Facts" || $name == "RepeatTag" || $name == "List" || $name == "Relatives")) {
- if (isset($elementHandler[$name]['end'])) {
- if ($elementHandler[$name]['end'] != "") {
- call_user_func($elementHandler[$name]['end']);
- }
- } elseif (!isset($elementHandler[$name]['start'])) {
- htmlEndHandler($name);
- }
- }
-}
-
-/**
- * XML character data handler
- *
- * This function is called whenever raw character data is reached
- * just print it to the screen
- *
- * @param resource $parser the resource handler for the XML parser
- * @param string $data the name of the XML element parsed
- */
-function characterData($parser, $data) {
- global $printData, $currentElement, $processGedcoms, $processIfs, $processRepeats, $reportTitle, $wt_report, $reportDescription;
-
- if ($printData && ($processGedcoms == 0) && ($processIfs == 0) && ($processRepeats == 0)) {
- $currentElement->addText($data);
- } elseif ($reportTitle) {
- $wt_report->addTitle($data);
- } elseif ($reportDescription) {
- $wt_report->addDescription($data);
- }
-}
-
-/**
- * XML <style> start element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function styleStartHandler($attrs) {
- global $wt_report;
-
- if (empty($attrs['name'])) {
- throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
- }
-
- // array Style that will be passed on
- $s = array();
-
- // string Name af the style
- $s['name'] = $attrs['name'];
-
- // string Name of the DEFAULT font
- $s['font'] = $wt_report->defaultFont;
- if (!empty($attrs['font'])) {
- $s['font'] = $attrs['font'];
- }
-
- // int The size of the font in points
- $s['size'] = $wt_report->defaultFontSize;
- if (!empty($attrs['size'])) {
- $s['size'] = (int) $attrs['size'];
- } // Get it as int to ignore all decimal points or text (if any text then int(0))
-
- // string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
- $s['style'] = "";
- if (!empty($attrs['style'])) {
- $s['style'] = $attrs['style'];
- }
-
- $wt_report->addStyle($s);
-}
-
-/**
- * XML <Doc> start element handler
- *
- * Sets up the basics of the document proparties
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function docStartHandler($attrs) {
- global $parser, $xml_parser, $wt_report;
-
- $parser = $xml_parser;
-
- // Custom page width
- if (!empty($attrs['customwidth'])) {
- $wt_report->pagew = (int) $attrs['customwidth'];
- } // Get it as int to ignore all decimal points or text (if any text then int(0))
- // Custom Page height
- if (!empty($attrs['customheight'])) {
- $wt_report->pageh = (int) $attrs['customheight'];
- } // Get it as int to ignore all decimal points or text (if any text then int(0))
-
- // Left Margin
- if (isset($attrs['leftmargin'])) {
- if ($attrs['leftmargin'] === "0") {
- $wt_report->leftmargin = 0;
- } elseif (!empty($attrs['leftmargin'])) {
- $wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
- }
- }
- // Right Margin
- if (isset($attrs['rightmargin'])) {
- if ($attrs['rightmargin'] === "0") {
- $wt_report->rightmargin = 0;
- } elseif (!empty($attrs['rightmargin'])) {
- $wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
- }
- }
- // Top Margin
- if (isset($attrs['topmargin'])) {
- if ($attrs['topmargin'] === "0") {
- $wt_report->topmargin = 0;
- } elseif (!empty($attrs['topmargin'])) {
- $wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
- }
- }
- // Bottom Margin
- if (isset($attrs['bottommargin'])) {
- if ($attrs['bottommargin'] === "0") {
- $wt_report->bottommargin = 0;
- } elseif (!empty($attrs['bottommargin'])) {
- $wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
- }
- }
- // Header Margin
- if (isset($attrs['headermargin'])) {
- if ($attrs['headermargin'] === "0") {
- $wt_report->headermargin = 0;
- } elseif (!empty($attrs['headermargin'])) {
- $wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
- }
- }
- // Footer Margin
- if (isset($attrs['footermargin'])) {
- if ($attrs['footermargin'] === "0") {
- $wt_report->footermargin = 0;
- } elseif (!empty($attrs['footermargin'])) {
- $wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
- }
- }
-
- // Page Orientation
- if (!empty($attrs['orientation'])) {
- if ($attrs['orientation'] == "landscape") {
- $wt_report->orientation = "landscape";
- } elseif ($attrs['orientation'] == "portrait") {
- $wt_report->orientation = "portrait";
- }
- }
- // Page Size
- if (!empty($attrs['pageSize'])) {
- $wt_report->pageFormat = strtoupper($attrs['pageSize']);
- }
-
- // Show Generated By...
- if (isset($attrs['showGeneratedBy'])) {
- if ($attrs['showGeneratedBy'] === "0") {
- $wt_report->showGenText = false;
- } elseif ($attrs['showGeneratedBy'] === "1") {
- $wt_report->showGenText = true;
- }
- }
-
- $wt_report->setup();
-}
-
-/**
- * XML </Doc> end element handler
- */
-function docEndHandler() {
- global $wt_report;
- $wt_report->run();
-}
-
-/**
- * XML <Header> start element handler
- */
-function headerStartHandler() {
- global $wt_report;
-
- // Clear the Header before any new elements are added
- $wt_report->clearHeader();
- $wt_report->setProcessing("H");
-}
-
-/**
- * XML <PageHeader> start element handler
- */
-function pageHeaderStartHandler() {
- global $printDataStack, $printData, $wt_reportStack, $wt_report, $ReportRoot;
-
- array_push($printDataStack, $printData);
- $printData = false;
- array_push($wt_reportStack, $wt_report);
- $wt_report = $ReportRoot->createPageHeader();
-}
-
-/**
- * XML <pageHeaderEndHandler> end element handler
- */
-function pageHeaderEndHandler() {
- global $printData, $printDataStack, $wt_report, $currentElement, $wt_reportStack;
-
- $printData = array_pop($printDataStack);
- $currentElement = $wt_report;
- $wt_report = array_pop($wt_reportStack);
- $wt_report->addElement($currentElement);
-}
-
-/**
- * XML <bodyStartHandler> start element handler
- */
-function bodyStartHandler() {
- global $wt_report;
- $wt_report->setProcessing("B");
-}
-
-/**
- * XML <footerStartHandler> start element handler
- */
-function footerStartHandler() {
- global $wt_report;
- $wt_report->setProcessing("F");
-}
-
-/**
- * XML <Cell> start element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function cellStartHandler($attrs) {
- global $printData, $printDataStack, $currentElement, $ReportRoot, $wt_report;
-
- // string The text alignment of the text in this box.
- $align = "";
- if (!empty($attrs['align'])) {
- $align = $attrs['align'];
- // RTL supported left/right alignment
- if ($align == "rightrtl") {
- if ($wt_report->rtl) {
- $align = "left";
- } else {
- $align = "right";
- }
- } elseif ($align == "leftrtl") {
- if ($wt_report->rtl) {
- $align = "right";
- } else {
- $align = "left";
- }
- }
- }
-
- // string The color to fill the background of this cell
- $bgcolor = "";
- if (!empty($attrs['bgcolor'])) {
- $bgcolor = $attrs['bgcolor'];
- }
-
- // int Whether or not the background should be painted
- $fill = 1;
- if (isset($attrs['fill'])) {
- if ($attrs['fill'] === "0") {
- $fill = 0;
- } elseif ($attrs['fill'] === "1") {
- $fill = 1;
- }
- }
-
- $reseth = true;
- // boolean if true reset the last cell height (default true)
- if (isset($attrs['reseth'])) {
- if ($attrs['reseth'] === "0") {
- $reseth = false;
- } elseif ($attrs['reseth'] === "1") {
- $reseth = true;
- }
- }
-
- // mixed Whether or not a border should be printed around this box
- $border = 0;
- if (!empty($attrs['border'])) {
- $border = $attrs['border'];
- }
- // string Border color in HTML code
- $bocolor = "";
- if (!empty($attrs['bocolor'])) {
- $bocolor = $attrs['bocolor'];
- }
-
- // int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
- $height = 0;
- if (!empty($attrs['height'])) {
- $height = (int) $attrs['height'];
- }
- // int Cell width (expressed in points) Setting the width to 0 will make it the width from the current location to the right margin.
- $width = 0;
- if (!empty($attrs['width'])) {
- $width = (int) $attrs['width'];
- }
-
- // int Stretch carachter mode
- $stretch = 0;
- if (!empty($attrs['stretch'])) {
- $stretch = (int) $attrs['stretch'];
- }
-
- // mixed Position the left corner of this box on the page. The default is the current position.
- $left = ".";
- if (isset($attrs['left'])) {
- if ($attrs['left'] === ".") {
- $left = ".";
- } elseif (!empty($attrs['left'])) {
- $left = (int) $attrs['left'];
- } elseif ($attrs['left'] === "0") {
- $left = 0;
- }
- }
- // mixed Position the top corner of this box on the page. the default is the current position
- $top = ".";
- if (isset($attrs['top'])) {
- if ($attrs['top'] === ".") {
- $top = ".";
- } elseif (!empty($attrs['top'])) {
- $top = (int) $attrs['top'];
- } elseif ($attrs['top'] === "0") {
- $top = 0;
- }
- }
-
- // string The name of the Style that should be used to render the text.
- $style = "";
- if (!empty($attrs['style'])) {
- $style = $attrs['style'];
- }
-
- // string Text color in html code
- $tcolor = "";
- if (!empty($attrs['tcolor'])) {
- $tcolor = $attrs['tcolor'];
- }
-
- // int Indicates where the current position should go after the call.
- $ln = 0;
- if (isset($attrs['newline'])) {
- if (!empty($attrs['newline'])) {
- $ln = (int) $attrs['newline'];
- } elseif ($attrs['newline'] === "0") {
- $ln = 0;
- }
- }
-
- if ($align == "left") {
- $align = "L";
- } elseif ($align == "right") {
- $align = "R";
- } elseif ($align == "center") {
- $align = "C";
- } elseif ($align == "justify") {
- $align = "J";
- }
-
- array_push($printDataStack, $printData);
- $printData = true;
-
- $currentElement = $ReportRoot->createCell(
- $width,
- $height,
- $border,
- $align,
- $bgcolor,
- $style,
- $ln,
- $top,
- $left,
- $fill,
- $stretch,
- $bocolor,
- $tcolor,
- $reseth
- );
-}
-
-/**
- * XML </Cell> end element handler
- */
-function cellEndHandler() {
- global $printData, $printDataStack, $currentElement, $wt_report;
-
- $printData = array_pop($printDataStack);
- $wt_report->addElement($currentElement);
-}
-
-/**
- * XML <Now /> element handler
- */
-function nowStartHandler() {
- global $currentElement;
-
- $g = timestamp_to_gedcom_date(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
- $currentElement->addText($g->display());
-}
-
-/**
- * XML <PageNum /> element handler
- */
-function pageNumStartHandler() {
- global $currentElement;
- $currentElement->addText("#PAGENUM#");
-}
-
-/**
- * XML <TotalPages /> element handler
- */
-function totalPagesStartHandler() {
- global $currentElement;
- $currentElement->addText("{{:ptp:}}");
-}
-
-/**
- * Called at the start of an element.
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function gedcomStartHandler($attrs) {
- global $vars, $gedrec, $gedrecStack, $processGedcoms, $fact, $desc, $ged_level, $WT_TREE;
-
- if ($processGedcoms > 0) {
- $processGedcoms++;
-
- return;
- }
-
- $tag = $attrs['id'];
- $tag = str_replace("@fact", $fact, $tag);
- $tags = explode(":", $tag);
- $newgedrec = '';
- if (count($tags) < 2) {
- $tmp = GedcomRecord::getInstance($attrs['id'], $WT_TREE);
- $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
- }
- if (empty($newgedrec)) {
- $tgedrec = $gedrec;
- $newgedrec = '';
- foreach ($tags as $tag) {
- if (preg_match("/\\$(.+)/", $tag, $match)) {
- if (isset($vars[$match[1]]['gedcom'])) {
- $newgedrec = $vars[$match[1]]['gedcom'];
- } else {
- $tmp = GedcomRecord::getInstance($match[1], $WT_TREE);
- $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
- }
- } else {
- if (preg_match("/@(.+)/", $tag, $match)) {
- $gmatch = array();
- if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) {
- $tmp = GedcomRecord::getInstance($gmatch[1], $WT_TREE);
- $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
- $tgedrec = $newgedrec;
- } else {
- $newgedrec = '';
- break;
- }
- } else {
- $temp = explode(" ", trim($tgedrec));
- $level = $temp[0] + 1;
- $newgedrec = get_sub_record($level, "$level $tag", $tgedrec);
- $tgedrec = $newgedrec;
- }
- }
- }
- }
- if (!empty($newgedrec)) {
- array_push($gedrecStack, array($gedrec, $fact, $desc));
- $gedrec = $newgedrec;
- if (preg_match("/(\d+) (_?[A-Z0-9]+) (.*)/", $gedrec, $match)) {
- $ged_level = $match[1];
- $fact = $match[2];
- $desc = trim($match[3]);
- }
- } else {
- $processGedcoms++;
- }
-}
-
-/**
- * Called at the end of an element.
- */
-function gedcomEndHandler() {
- global $gedrec, $gedrecStack, $processGedcoms, $fact, $desc;
-
- if ($processGedcoms > 0) {
- $processGedcoms--;
- } else {
- $temp = array_pop($gedrecStack);
- $gedrec = $temp[0];
- $fact = $temp[1];
- $desc = $temp[2];
- }
-}
-
-/**
- * XML <textBoxStartHandler> start element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function textBoxStartHandler($attrs) {
- global $printData, $printDataStack, $wt_report, $wt_reportStack, $ReportRoot;
-
- // string Background color code
- $bgcolor = "";
- if (!empty($attrs['bgcolor'])) {
- $bgcolor = $attrs['bgcolor'];
- }
-
- // boolean Wether or not fill the background color
- $fill = true;
- if (isset($attrs['fill'])) {
- if ($attrs['fill'] === "0") {
- $fill = false;
- } elseif ($attrs['fill'] === "1") {
- $fill = true;
- }
- }
-
- // var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
- $border = false;
- if (isset($attrs['border'])) {
- if ($attrs['border'] === "1") {
- $border = true;
- } elseif ($attrs['border'] === "0") {
- $border = false;
- }
- }
-
- // int The starting height of this cell. If the text wraps the height will automatically be adjusted
- $height = 0;
- if (!empty($attrs['height'])) {
- $height = (int) $attrs['height'];
- }
- // int Setting the width to 0 will make it the width from the current location to the margin
- $width = 0;
- if (!empty($attrs['width'])) {
- $width = (int) $attrs['width'];
- }
-
- // mixed Position the left corner of this box on the page. The default is the current position.
- $left = ".";
- if (isset($attrs['left'])) {
- if ($attrs['left'] === ".") {
- $left = ".";
- } elseif (!empty($attrs['left'])) {
- $left = (int) $attrs['left'];
- } elseif ($attrs['left'] === "0") {
- $left = 0;
- }
- }
- // mixed Position the top corner of this box on the page. the default is the current position
- $top = ".";
- if (isset($attrs['top'])) {
- if ($attrs['top'] === ".") {
- $top = ".";
- } elseif (!empty($attrs['top'])) {
- $top = (int) $attrs['top'];
- } elseif ($attrs['top'] === "0") {
- $top = 0;
- }
- }
- // boolean After this box is finished rendering, should the next section of text start immediately after the this box or should it start on a new line under this box. 0 = no new line, 1 = force new line. Default is 0
- $newline = false;
- if (isset($attrs['newline'])) {
- if ($attrs['newline'] === "1") {
- $newline = true;
- } elseif ($attrs['newline'] === "0") {
- $newline = false;
- }
- }
- // boolean
- $pagecheck = true;
- if (isset($attrs['pagecheck'])) {
- if ($attrs['pagecheck'] === "0") {
- $pagecheck = false;
- } elseif ($attrs['pagecheck'] === "1") {
- $pagecheck = true;
- }
- }
- // boolean Cell padding
- $padding = true;
- if (isset($attrs['padding'])) {
- if ($attrs['padding'] === "0") {
- $padding = false;
- } elseif ($attrs['padding'] === "1") {
- $padding = true;
- }
- }
- // boolean Reset this box Height
- $reseth = false;
- if (isset($attrs['reseth'])) {
- if ($attrs['reseth'] === "1") {
- $reseth = true;
- } elseif ($attrs['reseth'] === "0") {
- $reseth = false;
- }
- }
-
- // string Style of rendering
- $style = "";
-
- array_push($printDataStack, $printData);
- $printData = false;
-
- array_push($wt_reportStack, $wt_report);
- $wt_report = $ReportRoot->createTextBox(
- $width,
- $height,
- $border,
- $bgcolor,
- $newline,
- $left,
- $top,
- $pagecheck,
- $style,
- $fill,
- $padding,
- $reseth
- );
-}
-
-/**
- * XML <textBoxEndHandler> end element handler
- */
-function textBoxEndHandler() {
- global $printData, $printDataStack, $wt_report, $currentElement, $wt_reportStack;
-
- $printData = array_pop($printDataStack);
- $currentElement = $wt_report;
- $wt_report = array_pop($wt_reportStack);
- $wt_report->addElement($currentElement);
-}
-
-/**
- * @param array $attrs an array of key value pairs for the attributes
- */
-function textStartHandler($attrs) {
- global $printData, $printDataStack, $currentElement, $ReportRoot;
-
- array_push($printDataStack, $printData);
- $printData = true;
-
- // string The name of the Style that should be used to render the text.
- $style = "";
- if (!empty($attrs['style'])) {
- $style = $attrs['style'];
- }
-
- // string The color of the text - Keep the black color as default
- $color = "";
- if (!empty($attrs['color'])) {
- $color = $attrs['color'];
- }
-
- $currentElement = $ReportRoot->createText($style, $color);
-}
-
-/**
- *
- */
-function textEndHandler() {
- global $printData, $printDataStack, $wt_report, $currentElement;
-
- $printData = array_pop($printDataStack);
- $wt_report->addElement($currentElement);
-}
-
-/**
- * XML <GetPersonName> start element handler
- * Get the name
- * 1. id is empty - current GEDCOM record
- * 2. id is set with a record id
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function getPersonNameStartHandler($attrs) {
- // @deprecated
- global $currentElement, $vars, $gedrec, $WT_TREE;
-
- $id = "";
- $match = array();
- if (empty($attrs['id'])) {
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $id = $match[1];
- }
- } else {
- if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
- if (isset($vars[$match[1]]['id'])) {
- $id = $vars[$match[1]]['id'];
- }
- } else {
- if (preg_match("/@(.+)/", $attrs['id'], $match)) {
- $gmatch = array();
- if (preg_match("/\d $match[1] @([^@]+)@/", $gedrec, $gmatch)) {
- $id = $gmatch[1];
- }
- } else {
- $id = $attrs['id'];
- }
- }
- }
- if (!empty($id)) {
- $record = GedcomRecord::getInstance($id, $WT_TREE);
- if (is_null($record)) {
- return;
- }
- if (!$record->canShowName()) {
- $currentElement->addText(I18N::translate('Private'));
- } else {
- $name = $record->getFullName();
- $name = preg_replace(
- array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
- array('«', '', '»'),
- $name
- );
- $name = strip_tags($name);
- if (!empty($attrs['truncate'])) {
- if (mb_strlen($name) > $attrs['truncate']) {
- $name = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
- $words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
- $name = $words[count($words) - 1];
- for ($i = count($words) - 2; $i >= 0; $i--) {
- $len = mb_strlen($name);
- for ($j = count($words) - 3; $j >= 0; $j--) {
- $len += mb_strlen($words[$j]);
- }
- if ($len > $attrs['truncate']) {
- $first_letter = mb_substr($words[$i], 0, 1);
- // Do not show " of nick-names
- if ($first_letter != "\"") {
- $name = mb_substr($words[$i], 0, 1) . '. ' . $name;
- }
- } else {
- $name = $words[$i] . ' ' . $name;
- }
- }
- }
- } else {
- $addname = $record->getAddName();
- $addname = preg_replace(
- array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
- array('«', '', '»'),
- $addname
- );
- $addname = strip_tags($addname);
- if (!empty($addname)) {
- $name .= " " . $addname;
- }
- }
- $currentElement->addText(trim($name));
- }
- }
-}
-
-/**
- * XML <GedcomValue> start element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function gedcomValueStartHandler($attrs) {
- // @deprecated
- global $currentElement, $gedrec, $fact, $desc, $WT_TREE;
-
- $id = "";
- $match = array();
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $id = $match[1];
- }
-
- if (isset($attrs['newline']) && $attrs['newline'] == "1") {
- $useBreak = "1";
- } else {
- $useBreak = "0";
- }
-
- $tag = $attrs['tag'];
- if (!empty($tag)) {
- if ($tag == "@desc") {
- $value = $desc;
- $value = trim($value);
- $currentElement->addText($value);
- }
- if ($tag == "@id") {
- $currentElement->addText($id);
- } else {
- $tag = str_replace("@fact", $fact, $tag);
- if (empty($attrs['level'])) {
- $temp = explode(" ", trim($gedrec));
- $level = $temp[0];
- if ($level == 0) {
- $level++;
- }
- } else {
- $level = $attrs['level'];
- }
- $tags = preg_split('/[: ]/', $tag);
- $value = get_gedcom_value($tag, $level, $gedrec);
- switch (end($tags)) {
- case 'DATE':
- $tmp = new Date($value);
- $value = $tmp->display();
- break;
- case 'PLAC':
- $tmp = new Place($value, $WT_TREE);
- $value = $tmp->getShortName();
- break;
- }
- if ($useBreak == "1") {
- // Insert <br> when multiple dates exist.
- // This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
- $value = str_replace('(', '<br>(', $value);
- $value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
- $value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
- if (substr($value, 0, 6) == '<br>') {
- $value = substr($value, 6);
- }
- }
- $currentElement->addText($value);
- }
- }
-}
-
-/**
- * XML <RepeatTag> start element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function repeatTagStartHandler($attrs) {
- // @deprecated
- global $repeats, $repeatsStack, $gedrec, $repeatBytes, $parser, $processRepeats, $fact, $desc, $currentElement;
-
- $processRepeats++;
- if ($processRepeats > 1) {
- return;
- }
-
- array_push($repeatsStack, array($repeats, $repeatBytes));
- $repeats = array();
- $repeatBytes = xml_get_current_line_number($parser);
-
- $tag = "";
- if (isset($attrs['tag'])) {
- $tag = $attrs['tag'];
- }
- if (!empty($tag)) {
- if ($tag == "@desc") {
- $value = $desc;
- $value = trim($value);
- $currentElement->addText($value);
- } else {
- $tag = str_replace("@fact", $fact, $tag);
- $tags = explode(":", $tag);
- $temp = explode(" ", trim($gedrec));
- $level = $temp[0];
- if ($level == 0) {
- $level++;
- }
- $subrec = $gedrec;
- $t = $tag;
- $count = count($tags);
- $i = 0;
- while ($i < $count) {
- $t = $tags[$i];
- if (!empty($t)) {
- if ($i < ($count - 1)) {
- $subrec = get_sub_record($level, "$level $t", $subrec);
- if (empty($subrec)) {
- $level--;
- $subrec = get_sub_record($level, "@ $t", $gedrec);
- if (empty($subrec)) {
- return;
- }
- }
- }
- $level++;
- }
- $i++;
- }
- $level--;
- $count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
- $i = 0;
- while ($i < $count) {
- $repeats[] = get_sub_record($level, "$level $t", $subrec, $i + 1);
- $i++;
- }
- }
- }
-}
-
-/**
- * XML </ RepeatTag> end element handler
- */
-function repeatTagEndHandler() {
- global $processRepeats, $repeats, $repeatsStack, $repeatBytes;
-
- $processRepeats--;
- if ($processRepeats > 0) {
- return;
- }
-
- // Check if there is anything to repeat
- if (count($repeats) > 0) {
- // No need to load them if not used...
- global $parser, $parserStack, $report, $gedrec;
-
- $lineoffset = 0;
- foreach ($repeatsStack as $rep) {
- $lineoffset += $rep[1];
- }
- //-- read the xml from the file
- $lines = file($report);
- while (strpos($lines[$lineoffset + $repeatBytes], "<RepeatTag") === false) {
- $lineoffset--;
- }
- $lineoffset++;
- $reportxml = "<tempdoc>\n";
- $line_nr = $lineoffset + $repeatBytes;
- // RepeatTag Level counter
- $count = 1;
- while (0 < $count) {
- if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
- $count++;
- } elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
- $count--;
- }
- if (0 < $count) {
- $reportxml .= $lines[$line_nr];
- }
- $line_nr++;
- }
- // No need to drag this
- unset($lines);
- $reportxml .= "</tempdoc>\n";
- // Save original values
- array_push($parserStack, $parser);
- $oldgedrec = $gedrec;
- foreach ($repeats as $gedrec) {
- //-- start the sax parser
- $repeat_parser = xml_parser_create();
- $parser = $repeat_parser;
- //-- make sure everything is case sensitive
- xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
- //-- set the main element handler functions
- xml_set_element_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\startElement', '\Fisharebest\Webtrees\Report\\endElement');
- //-- set the character data handler
- xml_set_character_data_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\characterData');
- if (!xml_parse($repeat_parser, $reportxml, true)) {
- throw new \DomainException(sprintf(
- 'RepeatTagEHandler XML error: %s at line %d',
- xml_error_string(xml_get_error_code($repeat_parser)),
- xml_get_current_line_number($repeat_parser)
- ));
- }
- xml_parser_free($repeat_parser);
- }
- // Restore original values
- $gedrec = $oldgedrec;
- $parser = array_pop($parserStack);
- }
- $temp = array_pop($repeatsStack);
- $repeats = $temp[0];
- $repeatBytes = $temp[1];
-}
-
-/**
- * Variable lookup
- *
- * Retrieve predefined variables :
- *
- * @ desc GEDCOM fact description, example:
- * 1 EVEN This is a description
- * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
- * $ I18N::translate('....')
- * $ language_settings[]
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function varStartHandler($attrs) {
- // @deprecated
- global $currentElement, $type, $parser;
- // Retrievable variables
- global $desc, $fact, $vars;
-
- if (empty($attrs['var'])) {
- throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($parser));
- }
-
- $var = $attrs['var'];
- // SetVar element preset variables
- if (!empty($vars[$var]['id'])) {
- $var = $vars[$var]['id'];
- } else {
- $tfact = $fact;
- if (($fact == "EVEN" || $fact == "FACT") && is_string($type) && $type != " ") {
- // Use :
- // n TYPE This text if string
- $tfact = $type;
- }
- $var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $desc), $var);
- if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
- $var = I18N::number($match[1]);
- } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
- $var = I18N::translate($match[1]);
- } elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
- $var = I18N::translateContext($match[1], $match[2]);
- }
- }
- // Check if variable is set as a date and reformat the date
- if (isset($attrs['date'])) {
- if ($attrs['date'] === "1") {
- $g = new Date($var);
- $var = $g->display();
- }
- }
- $currentElement->addText($var);
-}
-
-/**
- * @param array $attrs an array of key value pairs for the attributes
- */
-function factsStartHandler($attrs) {
- // @deprecated
- global $repeats, $repeatsStack, $gedrec, $parser, $repeatBytes, $processRepeats, $vars, $WT_TREE;
-
- $processRepeats++;
- if ($processRepeats > 1) {
- return;
- }
-
- array_push($repeatsStack, array($repeats, $repeatBytes));
- $repeats = array();
- $repeatBytes = xml_get_current_line_number($parser);
-
- $id = "";
- $match = array();
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $id = $match[1];
- }
- $tag = "";
- if (isset($attrs['ignore'])) {
- $tag .= $attrs['ignore'];
- }
- if (preg_match("/\\$(.+)/", $tag, $match)) {
- $tag = $vars[$match[1]]['id'];
- }
-
- $record = GedcomRecord::getInstance($id, $WT_TREE);
- if (empty($attrs['diff']) && !empty($id)) {
- $facts = $record->getFacts();
- sort_facts($facts);
- $repeats = array();
- $nonfacts = explode(',', $tag);
- foreach ($facts as $event) {
- if (!in_array($event->getTag(), $nonfacts)) {
- $repeats[] = $event->getGedcom();
- }
- }
- } else {
- foreach ($record->getFacts() as $fact) {
- if ($fact->isPendingAddition() && $fact->getTag() != 'CHAN') {
- $repeats[] = $fact->getGedcom();
- }
- }
- }
-}
-
-/**
- * XML </ Facts> end element handler
- */
-function factsEndHandler() {
- global $repeats, $repeatsStack, $repeatBytes, $parser, $parserStack, $report, $gedrec, $fact, $desc, $type, $processRepeats;
-
- $processRepeats--;
- if ($processRepeats > 0) {
- return;
- }
-
- // Check if there is anything to repeat
- if (count($repeats) > 0) {
-
- $line = xml_get_current_line_number($parser) - 1;
- $lineoffset = 0;
- foreach ($repeatsStack as $rep) {
- $lineoffset += $rep[1];
- }
-
- //-- read the xml from the file
- $lines = file($report);
- while ($lineoffset + $repeatBytes > 0 && strpos($lines[$lineoffset + $repeatBytes], '<Facts ') === false) {
- $lineoffset--;
- }
- $lineoffset++;
- $reportxml = "<tempdoc>\n";
- $i = $line + $lineoffset;
- $line_nr = $repeatBytes + $lineoffset;
- while ($line_nr < $i) {
- $reportxml .= $lines[$line_nr];
- $line_nr++;
- }
- // No need to drag this
- unset($lines);
- $reportxml .= "</tempdoc>\n";
- // Save original values
- array_push($parserStack, $parser);
- $oldgedrec = $gedrec;
- $count = count($repeats);
- $i = 0;
- while ($i < $count) {
- $gedrec = $repeats[$i];
- $fact = '';
- $desc = '';
- if (preg_match('/1 (\w+)(.*)/', $gedrec, $match)) {
- $fact = $match[1];
- if ($fact === 'EVEN' || $fact === 'FACT') {
- $tmatch = array();
- if (preg_match('/2 TYPE (.+)/', $gedrec, $tmatch)) {
- $type = trim($tmatch[1]);
- } else {
- $type = ' ';
- }
- }
- $desc = trim($match[2]);
- $desc .= get_cont(2, $gedrec);
- }
- //-- start the sax parser
- $repeat_parser = xml_parser_create();
- $parser = $repeat_parser;
- //-- make sure everything is case sensitive
- xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
- //-- set the main element handler functions
- xml_set_element_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\startElement', '\Fisharebest\Webtrees\Report\\endElement');
- //-- set the character data handler
- xml_set_character_data_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\characterData');
- if (!xml_parse($repeat_parser, $reportxml, true)) {
- throw new \DomainException(sprintf(
- 'FactsEHandler XML error: %s at line %d',
- xml_error_string(xml_get_error_code($repeat_parser)),
- xml_get_current_line_number($repeat_parser)
- ));
- }
- xml_parser_free($repeat_parser);
- $i++;
- }
- // Restore original values
- $parser = array_pop($parserStack);
- $gedrec = $oldgedrec;
- }
- $temp = array_pop($repeatsStack);
- $repeats = $temp[0];
- $repeatBytes = $temp[1];
-}
-
-/**
- * Setting upp or changing variables in the XML
- * The XML variable name and value is stored in the global variable $vars
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function setVarStartHandler($attrs) {
- global $vars, $gedrec, $fact, $desc, $generation;
-
- if (empty($attrs['name'])) {
- throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
- }
-
- $name = $attrs['name'];
- $value = $attrs['value'];
- $match = array();
- // Current GEDCOM record strings
- if ($value == "@ID") {
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $value = $match[1];
- }
- } elseif ($value == "@fact") {
- $value = $fact;
- } elseif ($value == "@desc") {
- $value = $desc;
- } elseif ($value == "@generation") {
- $value = $generation;
- } elseif (preg_match("/@(\w+)/", $value, $match)) {
- $gmatch = array();
- if (preg_match("/\d $match[1] (.+)/", $gedrec, $gmatch)) {
- $value = str_replace("@", "", trim($gmatch[1]));
- }
- }
- if (preg_match("/\\$(\w+)/", $name, $match)) {
- $name = $vars["'" . $match[1] . "'"]['id'];
- }
- $count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
- $i = 0;
- while ($i < $count) {
- $t = $vars[$match[$i][1]]['id'];
- $value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
- $i++;
- }
- if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
- $value = I18N::number($match[1]);
- } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
- $value = I18N::translate($match[1]);
- } elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
- $value = I18N::translateContext($match[1], $match[2]);
- }
- // Arithmetic functions
- if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
- switch ($match[2]) {
- case "+":
- $t = $match[1] + $match[3];
- $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
- break;
- case "-":
- $t = $match[1] - $match[3];
- $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
- break;
- case "*":
- $t = $match[1] * $match[3];
- $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
- break;
- case "/":
- $t = $match[1] / $match[3];
- $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
- break;
- }
- }
- if (strpos($value, "@") !== false) {
- $value = "";
- }
- $vars[$name]['id'] = $value;
-}
-
-/**
- * XML <if > start element
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function ifStartHandler($attrs) {
- global $vars, $gedrec, $processIfs, $fact, $desc, $generation;
-
- if ($processIfs > 0) {
- $processIfs++;
-
- return;
- }
-
- $condition = $attrs['condition'];
- $condition = preg_replace("/\\$(\w+)/", "\$vars[\"$1\"][\"id\"]", $condition);
- $condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
- // Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
- $condition = str_replace("@fact", $fact, $condition);
- $match = array();
- $count = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
- $i = 0;
- while ($i < $count) {
- $id = $match[$i][1];
- $value = "\"\"";
- if ($id == "ID") {
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $value = "'" . $match[1] . "'";
- }
- } elseif ($id == "fact") {
- $value = "\"$fact\"";
- } elseif ($id == "desc") {
- $value = "\"" . addslashes($desc) . "\"";
- } elseif ($id == "generation") {
- $value = "\"$generation\"";
- } else {
-
- $temp = explode(" ", trim($gedrec));
- $level = $temp[0];
- if ($level == 0) {
- $level++;
- }
- $value = get_gedcom_value($id, $level, $gedrec);
- if (empty($value)) {
- $level++;
- $value = get_gedcom_value($id, $level, $gedrec);
- }
- $value = preg_replace("/^@(" . WT_REGEX_XREF . ")@$/", "$1", $value);
- $value = "\"" . addslashes($value) . "\"";
- }
- $condition = str_replace("@$id", $value, $condition);
- $i++;
- }
- $condition = "return (bool) ($condition);";
- $ret = @eval($condition);
- if (!$ret) {
- $processIfs++;
- }
-}
-
-/**
- * XML <if /> end element
- */
-function ifEndHandler() {
- global $processIfs;
- if ($processIfs > 0) {
- $processIfs--;
- }
-}
-
-/**
- * XML <Footnote > start element
- * Collect the Footnote links
- * GEDCOM Records that are protected by Privacy setting will be ignore
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function footnoteStartHandler($attrs) {
- global $printData, $printDataStack, $currentElement, $footnoteElement, $processFootnote, $gedrec, $ReportRoot, $WT_TREE;
-
- $id = "";
- if (preg_match("/[0-9] (.+) @(.+)@/", $gedrec, $match)) {
- $id = $match[2];
- }
- $record = GedcomRecord::GetInstance($id, $WT_TREE);
- if ($record && $record->canShow()) {
- array_push($printDataStack, $printData);
- $printData = true;
- $style = "";
- if (!empty($attrs['style'])) {
- $style = $attrs['style'];
- }
- $footnoteElement = $currentElement;
- $currentElement = $ReportRoot->createFootnote($style);
- } else {
- $printData = false;
- $processFootnote = false;
- }
-}
-
-/**
- * XML <Footnote /> end element
- * Print the collected Footnote data
- */
-function footnoteEndHandler() {
- // @deprecated
- global $printData, $printDataStack, $currentElement, $footnoteElement, $processFootnote, $wt_report;
-
- if ($processFootnote) {
- $printData = array_pop($printDataStack);
- $temp = trim($currentElement->getValue());
- if (strlen($temp) > 3) {
- $wt_report->addElement($currentElement);
- }
- $currentElement = $footnoteElement;
- } else {
- $processFootnote = true;
- }
-}
-
-/**
- * XML <FootnoteTexts /> element
- */
-function footnoteTextsStartHandler() {
- global $wt_report;
-
- $temp = "footnotetexts";
- $wt_report->addElement($temp);
-}
-
-/**
- * XML <AgeAtDeath /> element handler
- */
-function ageAtDeathStartHandler() {
- // This duplicates functionality in format_fact_date()
- global $currentElement, $gedrec, $factrec, $WT_TREE;
-
- $match = array();
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $person = Individual::getInstance($match[1], $WT_TREE);
- // Recorded age
- if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
- $fact_age = $match[1];
- } else {
- $fact_age = '';
- }
- if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
- $husb_age = $match[1];
- } else {
- $husb_age = '';
- }
- if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
- $wife_age = $match[1];
- } else {
- $wife_age = '';
- }
-
- // Calculated age
- $birth_date = $person->getBirthDate();
- // Can't use getDeathDate(), as this also gives BURI/CREM events, which
- // wouldn't give the correct "days after death" result for people with
- // no DEAT.
- $death_event = $person->getFirstFact('DEAT');
- if ($death_event) {
- $death_date = $death_event->getDate();
- } else {
- $death_date = new Date('');
- }
- $value = '';
- if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
- $age = Date::getAgeGedcom($birth_date, $death_date);
- // Only show calculated age if it differs from recorded age
- if ($age != '' && $age != "0d") {
- if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
- ) {
- $value = get_age_at_event($age, false);
- $abbrev = substr($value, 0, strpos($value, ' ') + 5);
- if ($value !== $abbrev) {
- $value = $abbrev . '.';
- }
- }
- }
- }
- $currentElement->addText($value);
- }
-}
-
-/**
- * XML element Forced line break handler - HTML code
- */
-function brStartHandler() {
- global $printData, $currentElement, $processGedcoms;
- if ($printData && ($processGedcoms == 0)) {
- $currentElement->addText('<br>');
- }
-}
-
-/**
- * XML <sp />element Forced space handler
- */
-function spStartHandler() {
- global $printData, $currentElement, $processGedcoms;
- if ($printData && ($processGedcoms == 0)) {
- $currentElement->addText(' ');
- }
-}
-
-/**
- * @param array $attrs an array of key value pairs for the attributes
- */
-function highlightedImageStartHandler($attrs) {
- global $gedrec, $wt_report, $ReportRoot, $WT_TREE;
-
- $id = '';
- $match = array();
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $id = $match[1];
- }
-
- // mixed Position the top corner of this box on the page. the default is the current position
- $top = '.';
- if (isset($attrs['top'])) {
- if ($attrs['top'] === '0') {
- $top = 0;
- } elseif ($attrs['top'] === '.') {
- $top = '.';
- } elseif (!empty($attrs['top'])) {
- $top = (int) $attrs['top'];
- }
- }
-
- // mixed Position the left corner of this box on the page. the default is the current position
- $left = '.';
- if (isset($attrs['left'])) {
- if ($attrs['left'] === '0') {
- $left = 0;
- } elseif ($attrs['left'] === '.') {
- $left = '.';
- } elseif (!empty($attrs['left'])) {
- $left = (int) $attrs['left'];
- }
- }
-
- // string Align the image in left, center, right
- $align = '';
- if (!empty($attrs['align'])) {
- $align = $attrs['align'];
- }
-
- // string Next Line should be T:next to the image, N:next line
- $ln = '';
- if (!empty($attrs['ln'])) {
- $ln = $attrs['ln'];
- }
-
- $width = 0;
- $height = 0;
- if (!empty($attrs['width'])) {
- $width = (int) $attrs['width'];
- }
- if (!empty($attrs['height'])) {
- $height = (int) $attrs['height'];
- }
-
- $person = Individual::getInstance($id, $WT_TREE);
- $mediaobject = $person->findHighlightedMedia();
- if ($mediaobject) {
- $attributes = $mediaobject->getImageAttributes('thumb');
- if (in_array(
- $attributes['ext'],
- array(
- 'GIF',
- 'JPG',
- 'PNG',
- 'SWF',
- 'PSD',
- 'BMP',
- 'TIFF',
- 'TIFF',
- 'JPC',
- 'JP2',
- 'JPX',
- 'JB2',
- 'SWC',
- 'IFF',
- 'WBMP',
- 'XBM',
- )
- ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
- ) {
- if ($width > 0 && $height == 0) {
- $perc = $width / $attributes['adjW'];
- $height = round($attributes['adjH'] * $perc);
- } elseif ($height > 0 && $width == 0) {
- $perc = $height / $attributes['adjH'];
- $width = round($attributes['adjW'] * $perc);
- } else {
- $width = $attributes['adjW'];
- $height = $attributes['adjH'];
- }
- $image = $ReportRoot->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
- $wt_report->addElement($image);
- }
- }
-}
-
-/**
- * @param array $attrs an array of key value pairs for the attributes
- */
-function imageStartHandler($attrs) {
- global $gedrec, $wt_report, $ReportRoot, $WT_TREE;
-
- // mixed Position the top corner of this box on the page. the default is the current position
- $top = '.';
- if (isset($attrs['top'])) {
- if ($attrs['top'] === "0") {
- $top = 0;
- } elseif ($attrs['top'] === '.') {
- $top = '.';
- } elseif (!empty($attrs['top'])) {
- $top = (int) $attrs['top'];
- }
- }
-
- // mixed Position the left corner of this box on the page. the default is the current position
- $left = '.';
- if (isset($attrs['left'])) {
- if ($attrs['left'] === '0') {
- $left = 0;
- } elseif ($attrs['left'] === '.') {
- $left = '.';
- } elseif (!empty($attrs['left'])) {
- $left = (int) $attrs['left'];
- }
- }
-
- // string Align the image in left, center, right
- $align = '';
- if (!empty($attrs['align'])) {
- $align = $attrs['align'];
- }
-
- // string Next Line should be T:next to the image, N:next line
- $ln = 'T';
- if (!empty($attrs['ln'])) {
- $ln = $attrs['ln'];
- }
-
- $width = 0;
- $height = 0;
- if (!empty($attrs['width'])) {
- $width = (int) $attrs['width'];
- }
- if (!empty($attrs['height'])) {
- $height = (int) $attrs['height'];
- }
-
- $file = '';
- if (!empty($attrs['file'])) {
- $file = $attrs['file'];
- }
- if ($file == "@FILE") {
- $match = array();
- if (preg_match("/\d OBJE @(.+)@/", $gedrec, $match)) {
- $mediaobject = Media::getInstance($match[1], $WT_TREE);
- $attributes = $mediaobject->getImageAttributes('thumb');
- if (in_array(
- $attributes['ext'],
- array(
- 'GIF',
- 'JPG',
- 'PNG',
- 'SWF',
- 'PSD',
- 'BMP',
- 'TIFF',
- 'TIFF',
- 'JPC',
- 'JP2',
- 'JPX',
- 'JB2',
- 'SWC',
- 'IFF',
- 'WBMP',
- 'XBM',
- )
- ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
- ) {
- if ($width > 0 && $height == 0) {
- $perc = $width / $attributes['adjW'];
- $height = round($attributes['adjH'] * $perc);
- } elseif ($height > 0 && $width == 0) {
- $perc = $height / $attributes['adjH'];
- $width = round($attributes['adjW'] * $perc);
- } else {
- $width = $attributes['adjW'];
- $height = $attributes['adjH'];
- }
- $image = $ReportRoot->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
- $wt_report->addElement($image);
- }
- }
- } else {
- if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
- $size = getimagesize($file);
- if ($width > 0 && $height == 0) {
- $perc = $width / $size[0];
- $height = round($size[1] * $perc);
- } elseif ($height > 0 && $width == 0) {
- $perc = $height / $size[1];
- $width = round($size[0] * $perc);
- } else {
- $width = $size[0];
- $height = $size[1];
- }
- $image = $ReportRoot->createImage($file, $left, $top, $width, $height, $align, $ln);
- $wt_report->addElement($image);
- }
- }
-}
-
-/**
- * XML <Line> element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function lineStartHandler($attrs) {
- global $wt_report, $ReportRoot;
-
- // Start horizontal position, current position (default)
- $x1 = ".";
- if (isset($attrs['x1'])) {
- if ($attrs['x1'] === "0") {
- $x1 = 0;
- } elseif ($attrs['x1'] === ".") {
- $x1 = ".";
- } elseif (!empty($attrs['x1'])) {
- $x1 = (int) $attrs['x1'];
- }
- }
- // Start vertical position, current position (default)
- $y1 = ".";
- if (isset($attrs['y1'])) {
- if ($attrs['y1'] === "0") {
- $y1 = 0;
- } elseif ($attrs['y1'] === ".") {
- $y1 = ".";
- } elseif (!empty($attrs['y1'])) {
- $y1 = (int) $attrs['y1'];
- }
- }
- // End horizontal position, maximum width (default)
- $x2 = ".";
- if (isset($attrs['x2'])) {
- if ($attrs['x2'] === "0") {
- $x2 = 0;
- } elseif ($attrs['x2'] === ".") {
- $x2 = ".";
- } elseif (!empty($attrs['x2'])) {
- $x2 = (int) $attrs['x2'];
- }
- }
- // End vertical position
- $y2 = ".";
- if (isset($attrs['y2'])) {
- if ($attrs['y2'] === "0") {
- $y2 = 0;
- } elseif ($attrs['y2'] === ".") {
- $y2 = ".";
- } elseif (!empty($attrs['y2'])) {
- $y2 = (int) $attrs['y2'];
- }
- }
-
- $line = $ReportRoot->createLine($x1, $y1, $x2, $y2);
- $wt_report->addElement($line);
-}
-
-/**
- * XML <List> start element handler
- *
- * @param array $attrs an array of key value pairs for the attributes
- */
-function listStartHandler($attrs) {
- global $gedrec, $repeats, $repeatBytes, $list, $fact, $desc, $repeatsStack, $processRepeats, $parser, $vars, $sortby, $WT_TREE;
-
- $processRepeats++;
- if ($processRepeats > 1) {
- return;
- }
-
- $match = array();
- if (isset($attrs['sortby'])) {
- $sortby = $attrs['sortby'];
- if (preg_match("/\\$(\w+)/", $sortby, $match)) {
- $sortby = $vars[$match[1]]['id'];
- $sortby = trim($sortby);
- }
- } else {
- $sortby = "NAME";
- }
-
- if (isset($attrs['list'])) {
- $listname = $attrs['list'];
- } else {
- $listname = "individual";
- }
- // Some filters/sorts can be applied using SQL, while others require PHP
- switch ($listname) {
- case "pending":
- $rows = Database::prepare(
- "SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" . " FROM `##change`" . " WHERE (xref, change_id) IN (" . " SELECT xref, MAX(change_id)" . " FROM `##change`" . " WHERE status='pending' AND gedcom_id=?" . " GROUP BY xref" . " )"
- )->execute(array($WT_TREE->getTreeId()))->fetchAll();
- $list = array();
- foreach ($rows as $row) {
- $list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
- }
- break;
- case 'individual':
- $sql_select = "SELECT DISTINCT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
- $sql_join = "";
- $sql_where = " WHERE i_file = " . $WT_TREE->getTreeId();
- $sql_order_by = "";
- foreach ($attrs as $attr => $value) {
- if (strpos($attr, 'filter') === 0 && $value) {
- // Substitute global vars
- $value = preg_replace_callback(
- '/\$(\w+)/',
- function ($matches) use ($vars) {
- return $vars[$matches[1]]['id'];
- },
- $value
- );
- // Convert the various filters into SQL
- if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
- $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
- $sql_where .= " AND {$attr}.d_fact='{$match[1]}'";
- $date = new Date($match[3]);
- if ($match[2] == "LTE") {
- $sql_where .= " AND {$attr}.d_julianday2<=" . $date->minimumJulianDay();
- } else {
- $sql_where .= " AND {$attr}.d_julianday1>=" . $date->minimumJulianDay();
- }
- if ($sortby == $match[1]) {
- $sortby = "";
- $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
- }
- unset($attrs[$attr]); // This filter has been fully processed
- } elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
- // Do nothing, unless you have to
- if ($match[1] != '' || $sortby == 'NAME') {
- $sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
- // Search the DB only if there is any name supplied
- if ($match[1] != "") {
- $names = explode(" ", $match[1]);
- foreach ($names as $name) {
- $sql_where .= " AND {$attr}.n_full LIKE " . Database::quote("%{$name}%");
- }
- }
- // Let the DB do the name sorting even when no name was entered
- if ($sortby == "NAME") {
- $sortby = "";
- $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
- }
- }
- unset($attrs[$attr]); // This filter has been fully processed
- } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
- $sql_where .= " AND i_gedcom REGEXP '" . $match[1] . "'";
- unset($attrs[$attr]); // This filter has been fully processed
- } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
- $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=i_file)";
- $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=i_id)";
- $sql_where .= " AND {$attr}a.p_place LIKE " . Database::quote("%{$match[1]}%");
- // Don't unset this filter. This is just initial filtering
- } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
- $query = "";
- // Level 1 tag
- if ($match[1] !== '') {
- $query .= "%1 {$match[1]}%";
- }
- // Level 2 tag
- if ($match[2] !== '') {
- $query .= "%2 {$match[2]}%";
- }
- // Contains what?
- if ($match[3] !== '') {
- $query .= "%{$match[3]}%";
- }
- $sql_where .= " AND i_gedcom LIKE " . Database::quote($query);
- // Don't unset this filter. This is just initial filtering
- }
- }
- }
-
- $list = array();
- $rows = Database::prepare(
- $sql_select . $sql_join . $sql_where . $sql_order_by
- )->fetchAll();
-
- foreach ($rows as $row) {
- $list[] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
- }
- break;
-
- case 'family':
- $sql_select = "SELECT DISTINCT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
- $sql_join = "";
- $sql_where = " WHERE f_file=" . $WT_TREE->getTreeId();
- $sql_order_by = "";
- foreach ($attrs as $attr => $value) {
- if (strpos($attr, 'filter') === 0 && $value) {
- // Substitute global vars
- $value = preg_replace_callback(
- '/\$(\w+)/',
- function ($matches) use ($vars) {
- return $vars[$matches[1]]['id'];
- },
- $value
- );
- // Convert the various filters into SQL
- if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
- $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
- $sql_where .= " AND {$attr}.d_fact='{$match[1]}'";
- $date = new Date($match[3]);
- if ($match[2] == "LTE") {
- $sql_where .= " AND {$attr}.d_julianday2<=" . $date->minimumJulianDay();
- } else {
- $sql_where .= " AND {$attr}.d_julianday1>=" . $date->minimumJulianDay();
- }
- if ($sortby == $match[1]) {
- $sortby = "";
- $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
- }
- unset($attrs[$attr]); // This filter has been fully processed
- } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
- $sql_where .= " AND f_gedcom REGEXP '" . $match[1] . "'";
- unset($attrs[$attr]); // This filter has been fully processed
- } elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
- $sql_join .= " JOIN `##link` AS {$attr}a ON ({$attr}a.l_file=f_file AND {$attr}a.l_from=f_id)";
- $sql_join .= " JOIN `##name` AS {$attr}b ON ({$attr}b.n_file=f_file AND n_id=f_id)";
- $sql_where .= " AND {$attr}a.l_type=IN ('HUSB, 'WIFE')";
- $sql_where .= " AND {$attr}.n_full LIKE " . Database::quote("%{$match[1]}%");
- if ($sortby == "NAME") {
- $sortby = "";
- $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
- }
- unset($attrs[$attr]); // This filter has been fully processed
- } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
- $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
- $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
- $sql_where .= " AND {$attr}a.p_place LIKE " . Database::quote("%{$match[1]}%");
- // Don't unset this filter. This is just initial filtering
- } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
- $query = "";
- // Level 1 tag
- if ($match[1] !== '') {
- $query .= "%1 {$match[1]}%";
- }
- // Level 2 tag
- if ($match[2] !== '') {
- $query .= "%2 {$match[2]}%";
- }
- // Contains what?
- if ($match[3] !== '') {
- $query .= "%{$match[3]}%";
- }
- $sql_where .= " AND f_gedcom LIKE " . Database::quote($query);
- // Don't unset this filter. This is just initial filtering
- }
- }
- }
-
- $list = array();
- $rows = Database::prepare(
- $sql_select . $sql_join . $sql_where . $sql_order_by
- )->fetchAll();
-
- foreach ($rows as $row) {
- $list[] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
- }
- break;
-
- default:
- throw new \DomainException('Invalid list name: ' . $listname);
- }
-
- $filters = array();
- $filters2 = array();
- if (isset($attrs['filter1']) && count($list) > 0) {
- foreach ($attrs as $key => $value) {
- if (preg_match("/filter(\d)/", $key)) {
- $condition = $value;
- if (preg_match("/@(\w+)/", $condition, $match)) {
- $id = $match[1];
- $value = "''";
- if ($id == "ID") {
- if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
- $value = "'" . $match[1] . "'";
- }
- } elseif ($id == "fact") {
- $value = "'" . $fact . "'";
- } elseif ($id == "desc") {
- $value = "'" . $desc . "'";
- } else {
- if (preg_match("/\d $id (.+)/", $gedrec, $match)) {
- $value = "'" . str_replace("@", "", trim($match[1])) . "'";
- }
- }
- $condition = preg_replace("/@$id/", $value, $condition);
- }
- //-- handle regular expressions
- if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
- $tag = trim($match[1]);
- $expr = trim($match[2]);
- $val = trim($match[3]);
- if (preg_match("/\\$(\w+)/", $val, $match)) {
- $val = $vars[$match[1]]['id'];
- $val = trim($val);
- }
- if ($val) {
- $searchstr = "";
- $tags = explode(":", $tag);
- //-- only limit to a level number if we are specifically looking at a level
- if (count($tags) > 1) {
- $level = 1;
- foreach ($tags as $t) {
- if (!empty($searchstr)) {
- $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
- }
- //-- search for both EMAIL and _EMAIL... silly double gedcom standard
- if ($t == "EMAIL" || $t == "_EMAIL") {
- $t = "_?EMAIL";
- }
- $searchstr .= $level . " " . $t;
- $level++;
- }
- } else {
- if ($tag == "EMAIL" || $tag == "_EMAIL") {
- $tag = "_?EMAIL";
- }
- $t = $tag;
- $searchstr = "1 " . $tag;
- }
- switch ($expr) {
- case "CONTAINS":
- if ($t == "PLAC") {
- $searchstr .= "[^\n]*[, ]*" . $val;
- } else {
- $searchstr .= "[^\n]*" . $val;
- }
- $filters[] = $searchstr;
- break;
- default:
- $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
- break;
- }
- }
- }
- }
- }
- }
- //-- apply other filters to the list that could not be added to the search string
- if ($filters) {
- foreach ($list as $key => $record) {
- foreach ($filters as $filter) {
- if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
- unset($list[$key]);
- break;
- }
- }
- }
- }
- if ($filters2) {
- $mylist = array();
- foreach ($list as $indi) {
- $key = $indi->getXref();
- $grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
- $keep = true;
- foreach ($filters2 as $filter) {
- if ($keep) {
- $tag = $filter['tag'];
- $expr = $filter['expr'];
- $val = $filter['val'];
- if ($val == "''") {
- $val = "";
- }
- $tags = explode(":", $tag);
- $t = end($tags);
- $v = get_gedcom_value($tag, 1, $grec);
- //-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
- if ($t == "EMAIL" && empty($v)) {
- $tag = str_replace("EMAIL", "_EMAIL", $tag);
- $tags = explode(":", $tag);
- $t = end($tags);
- $v = get_sub_record(1, $tag, $grec);
- }
-
- switch ($expr) {
- case "GTE":
- if ($t == "DATE") {
- $date1 = new Date($v);
- $date2 = new Date($val);
- $keep = (Date::compare($date1, $date2) >= 0);
- } elseif ($val >= $v) {
- $keep = true;
- }
- break;
- case "LTE":
- if ($t == "DATE") {
- $date1 = new Date($v);
- $date2 = new Date($val);
- $keep = (Date::compare($date1, $date2) <= 0);
- } elseif ($val >= $v) {
- $keep = true;
- }
- break;
- default:
- if ($v == $val) {
- $keep = true;
- } else {
- $keep = false;
- }
- break;
- }
- }
- }
- if ($keep) {
- $mylist[$key] = $indi;
- }
- }
- $list = $mylist;
- }
-
- switch ($sortby) {
- case 'NAME':
- uasort($list, '\Fisharebest\Webtrees\GedcomRecord::compare');
- break;
- case 'CHAN':
- uasort($list, function (GedcomRecord $x, GedcomRecord $y) {
- return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
- });
- break;
- case 'BIRT:DATE':
- uasort($list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
- break;
- case 'DEAT:DATE':
- uasort($list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
- break;
- case 'MARR:DATE':
- uasort($list, '\Fisharebest\Webtrees\\Family::compareMarrDate');
- break;
- default:
- // unsorted or already sorted by SQL
- break;
- }
-
- array_push($repeatsStack, array($repeats, $repeatBytes));
- $repeatBytes = xml_get_current_line_number($parser) + 1;
-}
-
-/**
- * XML <List> end element handler
- */
-function listEndHandler() {
- global $list, $repeats, $repeatsStack, $repeatBytes, $parser, $parserStack, $report, $gedrec, $processRepeats, $list_total, $list_private;
-
- $processRepeats--;
- if ($processRepeats > 0) {
- return;
- }
-
- // Check if there is any list
- if (count($list) > 0) {
- $lineoffset = 0;
- foreach ($repeatsStack as $rep) {
- $lineoffset += $rep[1];
- }
- //-- read the xml from the file
- $lines = file($report);
- while ((strpos($lines[$lineoffset + $repeatBytes], "<List") === false) && (($lineoffset + $repeatBytes) > 0)) {
- $lineoffset--;
- }
- $lineoffset++;
- $reportxml = "<tempdoc>\n";
- $line_nr = $lineoffset + $repeatBytes;
- // List Level counter
- $count = 1;
- while (0 < $count) {
- if (strpos($lines[$line_nr], "<List") !== false) {
- $count++;
- } elseif (strpos($lines[$line_nr], "</List") !== false) {
- $count--;
- }
- if (0 < $count) {
- $reportxml .= $lines[$line_nr];
- }
- $line_nr++;
- }
- // No need to drag this
- unset($lines);
- $reportxml .= "</tempdoc>";
- // Save original values
- array_push($parserStack, $parser);
- $oldgedrec = $gedrec;
-
- $list_total = count($list);
- $list_private = 0;
- foreach ($list as $record) {
- if ($record->canShow()) {
- $gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
- //-- start the sax parser
- $repeat_parser = xml_parser_create();
- $parser = $repeat_parser;
- //-- make sure everything is case sensitive
- xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
- //-- set the main element handler functions
- xml_set_element_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\startElement', '\Fisharebest\Webtrees\Report\\endElement');
- //-- set the character data handler
- xml_set_character_data_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\characterData');
- if (!xml_parse($repeat_parser, $reportxml, true)) {
- throw new \DomainException(sprintf(
- 'ListEHandler XML error: %s at line %d',
- xml_error_string(xml_get_error_code($repeat_parser)),
- xml_get_current_line_number($repeat_parser)
- ));
- }
- xml_parser_free($repeat_parser);
- } else {
- $list_private++;
- }
- }
- // Clean up the GLOBAL list array
- unset($list);
- $parser = array_pop($parserStack);
- $gedrec = $oldgedrec;
- }
- $temp = array_pop($repeatsStack);
- $repeats = $temp[0];
- $repeatBytes = $temp[1];
-}
-
-/**
- * XML <ListTotal> element handler
- *
- * Prints the total number of records in a list
- * The total number is collected from
- * List and Relatives
- */
-function listTotalStartHandler() {
- global $list_total, $list_private, $currentElement;
-
- if (empty($list_total)) {
- $list_total = 0;
- }
-
- if ($list_private == 0) {
- $currentElement->addText($list_total);
- } else {
- $currentElement->addText(($list_total - $list_private) . " / " . $list_total);
- }
-}
-
-/**
- * @param array $attrs an array of key value pairs for the attributes
- */
-function relativesStartHandler($attrs) {
- global $repeats, $repeatBytes, $list, $repeatsStack, $processRepeats, $parser, $vars, $sortby, $WT_TREE;
-
- $processRepeats++;
- if ($processRepeats > 1) {
- return;
- }
-
- $sortby = "NAME";
- if (isset($attrs['sortby'])) {
- $sortby = $attrs['sortby'];
- }
- $match = array();
- if (preg_match("/\\$(\w+)/", $sortby, $match)) {
- $sortby = $vars[$match[1]]['id'];
- $sortby = trim($sortby);
- }
-
- $maxgen = -1;
- if (isset($attrs['maxgen'])) {
- $maxgen = $attrs['maxgen'];
- }
- if ($maxgen == "*") {
- $maxgen = -1;
- }
-
- $group = "child-family";
- if (isset($attrs['group'])) {
- $group = $attrs['group'];
- }
- if (preg_match("/\\$(\w+)/", $group, $match)) {
- $group = $vars[$match[1]]['id'];
- $group = trim($group);
- }
-
- $id = "";
- if (isset($attrs['id'])) {
- $id = $attrs['id'];
- }
- if (preg_match("/\\$(\w+)/", $id, $match)) {
- $id = $vars[$match[1]]['id'];
- $id = trim($id);
- }
-
- $list = array();
- $person = Individual::getInstance($id, $WT_TREE);
- if (!empty($person)) {
- $list[$id] = $person;
- switch ($group) {
- case "child-family":
- foreach ($person->getChildFamilies() as $family) {
- $husband = $family->getHusband();
- $wife = $family->getWife();
- if (!empty($husband)) {
- $list[$husband->getXref()] = $husband;
- }
- if (!empty($wife)) {
- $list[$wife->getXref()] = $wife;
- }
- $children = $family->getChildren();
- foreach ($children as $child) {
- if (!empty($child)) {
- $list[$child->getXref()] = $child;
- }
- }
- }
- break;
- case "spouse-family":
- foreach ($person->getSpouseFamilies() as $family) {
- $husband = $family->getHusband();
- $wife = $family->getWife();
- if (!empty($husband)) {
- $list[$husband->getXref()] = $husband;
- }
- if (!empty($wife)) {
- $list[$wife->getXref()] = $wife;
- }
- $children = $family->getChildren();
- foreach ($children as $child) {
- if (!empty($child)) {
- $list[$child->getXref()] = $child;
- }
- }
- }
- break;
- case "direct-ancestors":
- add_ancestors($list, $id, false, $maxgen);
- break;
- case "ancestors":
- add_ancestors($list, $id, true, $maxgen);
- break;
- case "descendants":
- $list[$id]->generation = 1;
- add_descendancy($list, $id, false, $maxgen);
- break;
- case "all":
- add_ancestors($list, $id, true, $maxgen);
- add_descendancy($list, $id, true, $maxgen);
- break;
- }
- }
-
- switch ($sortby) {
- case 'NAME':
- uasort($list, '\Fisharebest\Webtrees\GedcomRecord::compare');
- break;
- case 'BIRT:DATE':
- uasort($list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
- break;
- case 'DEAT:DATE':
- uasort($list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
- break;
- case 'generation':
- $newarray = array();
- reset($list);
- $genCounter = 1;
- while (count($newarray) < count($list)) {
- foreach ($list as $key => $value) {
- $generation = $value->generation;
- if ($generation == $genCounter) {
- $newarray[$key] = new \stdClass;
- $newarray[$key]->generation = $generation;
- }
- }
- $genCounter++;
- }
- $list = $newarray;
- break;
- default:
- // unsorted
- break;
- }
- array_push($repeatsStack, array($repeats, $repeatBytes));
- $repeatBytes = xml_get_current_line_number($parser) + 1;
-}
-
-/**
- * XML </ Relatives> end element handler
- */
-function relativesEndHandler() {
- global $list, $repeats, $repeatsStack, $repeatBytes, $parser, $parserStack, $report, $gedrec, $processRepeats, $list_total, $list_private, $generation, $WT_TREE;
-
- $processRepeats--;
- if ($processRepeats > 0) {
- return;
- }
-
- // Check if there is any relatives
- if (count($list) > 0) {
- $lineoffset = 0;
- foreach ($repeatsStack as $rep) {
- $lineoffset += $rep[1];
- }
- //-- read the xml from the file
- $lines = file($report);
- while ((strpos($lines[$lineoffset + $repeatBytes], "<Relatives") === false) && (($lineoffset + $repeatBytes) > 0)) {
- $lineoffset--;
- }
- $lineoffset++;
- $reportxml = "<tempdoc>\n";
- $line_nr = $lineoffset + $repeatBytes;
- // Relatives Level counter
- $count = 1;
- while (0 < $count) {
- if (strpos($lines[$line_nr], "<Relatives") !== false) {
- $count++;
- } elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
- $count--;
- }
- if (0 < $count) {
- $reportxml .= $lines[$line_nr];
- }
- $line_nr++;
- }
- // No need to drag this
- unset($lines);
- $reportxml .= "</tempdoc>\n";
- // Save original values
- array_push($parserStack, $parser);
- $oldgedrec = $gedrec;
-
- $list_total = count($list);
- $list_private = 0;
- foreach ($list as $key => $value) {
- if (isset($value->generation)) {
- $generation = $value->generation;
- }
- $tmp = GedcomRecord::getInstance($key, $WT_TREE);
- $gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
- //-- start the sax parser
- $repeat_parser = xml_parser_create();
- $parser = $repeat_parser;
- //-- make sure everything is case sensitive
- xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
- //-- set the main element handler functions
- xml_set_element_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\startElement', '\Fisharebest\Webtrees\Report\\endElement');
- //-- set the character data handler
- xml_set_character_data_handler($repeat_parser, '\Fisharebest\Webtrees\Report\\characterData');
-
- if (!xml_parse($repeat_parser, $reportxml, true)) {
- throw new \DomainException(sprintf("RelativesEHandler XML error: %s at line %d", xml_error_string(xml_get_error_code($repeat_parser)), xml_get_current_line_number($repeat_parser)));
- }
- xml_parser_free($repeat_parser);
- }
- // Clean up the GLOBAL list array
- unset($list);
- $parser = array_pop($parserStack);
- $gedrec = $oldgedrec;
- }
- $temp = array_pop($repeatsStack);
- $repeats = $temp[0];
- $repeatBytes = $temp[1];
-}
-
-/**
- * XML <Generation /> element handler
- *
- * Prints the number of generations
- */
-function generationStartHandler() {
- global $generation, $currentElement;
-
- if (empty($generation)) {
- $generation = 1;
- }
-
- $currentElement->addText($generation);
-}
-
-/**
- * XML <NewPage /> element handler
- *
- * Has to be placed in an element (header, pageheader, body or footer)
- */
-function newPageStartHandler() {
- global $wt_report;
-
- $temp = "addpage";
- $wt_report->addElement($temp);
-}
-
-/**
- * @param array $attrs an array of key value pairs for the attributes
- * @param string $tag HTML tag name
- */
-function htmlStartHandler($tag, $attrs) {
- global $printData, $printDataStack, $wt_reportStack, $wt_report, $currentElement, $ReportRoot;
-
- if ($tag == "tempdoc") {
- return;
- }
- array_push($wt_reportStack, $wt_report);
- $wt_report = $ReportRoot->createHTML($tag, $attrs);
- $currentElement = $wt_report;
-
- array_push($printDataStack, $printData);
- $printData = true;
-}
-
-/**
- * @param string $tag
- */
-function htmlEndHandler($tag) {
- global $printData, $printDataStack, $wt_report, $currentElement, $wt_reportStack;
- if ($tag == "tempdoc") {
- return;
- }
-
- $printData = array_pop($printDataStack);
- $currentElement = $wt_report;
- $wt_report = array_pop($wt_reportStack);
- if (!is_null($wt_report)) {
- $wt_report->addElement($currentElement);
- } else {
- $wt_report = $currentElement;
- }
-}
-
-/**
- * XML <titleStartHandler> start element handler
- */
-function titleStartHandler() {
- global $reportTitle;
- $reportTitle = true;
-}
-
-/**
- * XML </titleEndHandler> end element handler
- */
-function titleEndHandler() {
- global $reportTitle;
- $reportTitle = false;
-}
-
-/**
- * XML <descriptionStartHandler> start element handler
- */
-function descriptionStartHandler() {
- global $reportDescription;
- $reportDescription = true;
-}
-
-/**
- * XML </descriptionEndHandler> end element handler
- */
-function descriptionEndHandler() {
- global $reportDescription;
- $reportDescription = false;
-}
-
-/**
* get gedcom tag value
*
* @param string $tag The tag to find, use : to delineate subtags
diff --git a/app/Report/ReportParserBase.php b/app/Report/ReportParserBase.php
index 36e4b16557..3312566ffa 100644
--- a/app/Report/ReportParserBase.php
+++ b/app/Report/ReportParserBase.php
@@ -15,16 +15,14 @@ namespace Fisharebest\Webtrees\Report;
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Date;
-use Fisharebest\Webtrees\I18N;
/**
* Class ReportParserBase
*/
class ReportParserBase {
/** @var resource The XML parser */
- private $xml_parser;
+ protected $xml_parser;
/** @var string Text contents of tags */
protected $text = '';
@@ -61,8 +59,8 @@ class ReportParserBase {
* @param string $name The name of the xml element parsed
* @param string[] $attrs An array of key value pairs for the attributes
*/
- private function startElement($parser, $name, $attrs) {
- $method = strtolower($name) . 'StartHandler';
+ public function startElement($parser, $name, $attrs) {
+ $method = $name . 'StartHandler';
if (method_exists($this, $method)) {
$this->$method($attrs);
}
@@ -74,8 +72,8 @@ class ReportParserBase {
* @param resource $parser the resource handler for the xml parser
* @param string $name the name of the xml element parsed
*/
- private function endElement($parser, $name) {
- $method = strtolower($name) . 'EndHandler';
+ public function endElement($parser, $name) {
+ $method = $name . 'EndHandler';
if (method_exists($this, $method)) {
$this->$method();
}
@@ -87,7 +85,7 @@ class ReportParserBase {
* @param resource $parser The resource handler for the xml parser
* @param string $data The name of the xml element parsed
*/
- private function characterData($parser, $data) {
+ public function characterData($parser, $data) {
$this->text .= $data;
}
}
diff --git a/app/Report/ReportParserGenerate.php b/app/Report/ReportParserGenerate.php
new file mode 100644
index 0000000000..98763dd449
--- /dev/null
+++ b/app/Report/ReportParserGenerate.php
@@ -0,0 +1,2625 @@
+<?php
+namespace Fisharebest\Webtrees\Report;
+
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2015 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Database;
+use Fisharebest\Webtrees\Date;
+use Fisharebest\Webtrees\Family;
+use Fisharebest\Webtrees\GedcomRecord;
+use Fisharebest\Webtrees\GedcomTag;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Individual;
+use Fisharebest\Webtrees\Media;
+use Fisharebest\Webtrees\Place;
+
+/**
+ * Class ReportParserGenerate - parse a report.xml file and generate the report.
+ */
+class ReportParserGenerate extends ReportParserBase {
+ /** @var bool Are we processing a <Description> element */
+ private $report_description = false;
+
+ /** @var bool Are we processing a <Title> element */
+ private $report_title = false;
+
+ /** @var bool Are we collecting data from <Footnote> elements */
+ private $process_footnote = true;
+
+ /** @var bool Are we currently outputing data? */
+ private $print_data = false;
+
+ /** @var bool[] Push-down stack of $print_data */
+ private $print_data_stack = array();
+
+ /** @var int */
+ private $process_gedcoms = 0;
+
+ /** @var int */
+ private $process_ifs = 0;
+
+ /** @var int */
+ private $process_repeats = 0;
+
+ /** @var int Quantity of data to repeat during loops */
+ private $repeat_bytes = 0;
+
+ /** @var array[] Repeated data when iterating over loops */
+ private $repeats = array();
+
+ /** @var array[] Nested repeating data */
+ private $repeats_stack = array();
+
+ /** @var string The current GEDCOM record */
+ private $gedrec = '';
+
+ /** @var string[] Nested GEDCOM records */
+ private $gedrec_stack = array();
+
+ /** @var ReportBaseElement The currently processed element */
+ private $current_element;
+
+ /** @var ReportBaseElement The currently processed element */
+ private $footnote_element;
+
+ /** @var string The GEDCOM fact currently being processed */
+ private $fact = '';
+
+ /** @var string The GEDCOM value currently being processed */
+ private $desc = '';
+
+ /** @var string The GEDCOM type currently being processed */
+ private $type = '';
+
+ /** @var int The current generational level */
+ private $generation = 1;
+
+ /** @var array Source data for processing lists */
+ private $list = array();
+
+ /** @var int Number of items in lists */
+ private $list_total = 0;
+
+ /** @var int Number of items filtered from lists */
+ private $list_private = 0;
+
+ /** {@inheritDoc} */
+ public function __construct($report) {
+ $this->current_element = new ReportBaseElement;
+ parent::__construct($report);
+ }
+
+ /**
+ *XML start element handler
+ *
+ * This function is called whenever a starting element is reached
+ * The element handler will be called if found, otherwise it must be HTML
+ *
+ * @param resource $parser the resource handler for the XML parser
+ * @param string $name the name of the XML element parsed
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function startElement($parser, $name, $attrs) {
+ global $vars;
+
+ $newattrs = array();
+
+ foreach ($attrs as $key => $value) {
+ if (preg_match("/^\\$(\w+)$/", $value, $match)) {
+ if ((isset($vars[$match[1]]['id'])) && (!isset($vars[$match[1]]['gedcom']))) {
+ $value = $vars[$match[1]]['id'];
+ }
+ }
+ $newattrs[$key] = $value;
+ }
+ $attrs = $newattrs;
+ if ($this->process_footnote && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag")) {
+ $start_method = $name . 'StartHandler';
+ $end_method = $name . 'EndHandler';
+ if (method_exists($this, $start_method)) {
+ $this->$start_method($attrs);
+ } elseif (!method_exists($this, $end_method)) {
+ $this->htmlStartHandler($name, $attrs);
+ }
+ }
+ }
+
+ /**
+ * XML end element handler
+ *
+ * This function is called whenever an ending element is reached
+ * The element handler will be called if found, otherwise it must be HTML
+ *
+ * @param resource $parser the resource handler for the XML parser
+ * @param string $name the name of the XML element parsed
+ */
+ function endElement($parser, $name) {
+ if (($this->process_footnote || $name === "Footnote") && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag" || $name === "List" || $name === "Relatives")) {
+ $start_method = $name . 'StartHandler';
+ $end_method = $name . 'EndHandler';
+ if (method_exists($this, $end_method)) {
+ $this->$end_method();
+ } elseif (!method_exists($this, $start_method)) {
+ $this->htmlEndHandler($name);
+ }
+ }
+ }
+
+ /**
+ * XML character data handler
+ *
+ * @param resource $parser the resource handler for the XML parser
+ * @param string $data the name of the XML element parsed
+ */
+ function characterData($parser, $data) {
+ global $wt_report;
+
+ if ($this->print_data && ($this->process_gedcoms === 0) && ($this->process_ifs === 0) && ($this->process_repeats === 0)) {
+ $this->current_element->addText($data);
+ } elseif ($this->report_title) {
+ $wt_report->addTitle($data);
+ } elseif ($this->report_description) {
+ $wt_report->addDescription($data);
+ }
+ }
+
+ /**
+ * XML <style> start element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function styleStartHandler($attrs) {
+ global $wt_report;
+
+ if (empty($attrs['name'])) {
+ throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
+ }
+
+ // array Style that will be passed on
+ $s = array();
+
+ // string Name af the style
+ $s['name'] = $attrs['name'];
+
+ // string Name of the DEFAULT font
+ $s['font'] = $wt_report->defaultFont;
+ if (!empty($attrs['font'])) {
+ $s['font'] = $attrs['font'];
+ }
+
+ // int The size of the font in points
+ $s['size'] = $wt_report->defaultFontSize;
+ if (!empty($attrs['size'])) {
+ $s['size'] = (int) $attrs['size'];
+ } // Get it as int to ignore all decimal points or text (if any text then int(0))
+
+ // string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
+ $s['style'] = "";
+ if (!empty($attrs['style'])) {
+ $s['style'] = $attrs['style'];
+ }
+
+ $wt_report->addStyle($s);
+ }
+
+ /**
+ * XML <Doc> start element handler
+ *
+ * Sets up the basics of the document proparties
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function docStartHandler($attrs) {
+ global $parser, $wt_report;
+
+ $parser = $this->xml_parser;
+
+ // Custom page width
+ if (!empty($attrs['customwidth'])) {
+ $wt_report->pagew = (int) $attrs['customwidth'];
+ } // Get it as int to ignore all decimal points or text (if any text then int(0))
+ // Custom Page height
+ if (!empty($attrs['customheight'])) {
+ $wt_report->pageh = (int) $attrs['customheight'];
+ } // Get it as int to ignore all decimal points or text (if any text then int(0))
+
+ // Left Margin
+ if (isset($attrs['leftmargin'])) {
+ if ($attrs['leftmargin'] === "0") {
+ $wt_report->leftmargin = 0;
+ } elseif (!empty($attrs['leftmargin'])) {
+ $wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
+ }
+ }
+ // Right Margin
+ if (isset($attrs['rightmargin'])) {
+ if ($attrs['rightmargin'] === "0") {
+ $wt_report->rightmargin = 0;
+ } elseif (!empty($attrs['rightmargin'])) {
+ $wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
+ }
+ }
+ // Top Margin
+ if (isset($attrs['topmargin'])) {
+ if ($attrs['topmargin'] === "0") {
+ $wt_report->topmargin = 0;
+ } elseif (!empty($attrs['topmargin'])) {
+ $wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
+ }
+ }
+ // Bottom Margin
+ if (isset($attrs['bottommargin'])) {
+ if ($attrs['bottommargin'] === "0") {
+ $wt_report->bottommargin = 0;
+ } elseif (!empty($attrs['bottommargin'])) {
+ $wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
+ }
+ }
+ // Header Margin
+ if (isset($attrs['headermargin'])) {
+ if ($attrs['headermargin'] === "0") {
+ $wt_report->headermargin = 0;
+ } elseif (!empty($attrs['headermargin'])) {
+ $wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
+ }
+ }
+ // Footer Margin
+ if (isset($attrs['footermargin'])) {
+ if ($attrs['footermargin'] === "0") {
+ $wt_report->footermargin = 0;
+ } elseif (!empty($attrs['footermargin'])) {
+ $wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
+ }
+ }
+
+ // Page Orientation
+ if (!empty($attrs['orientation'])) {
+ if ($attrs['orientation'] == "landscape") {
+ $wt_report->orientation = "landscape";
+ } elseif ($attrs['orientation'] == "portrait") {
+ $wt_report->orientation = "portrait";
+ }
+ }
+ // Page Size
+ if (!empty($attrs['pageSize'])) {
+ $wt_report->pageFormat = strtoupper($attrs['pageSize']);
+ }
+
+ // Show Generated By...
+ if (isset($attrs['showGeneratedBy'])) {
+ if ($attrs['showGeneratedBy'] === "0") {
+ $wt_report->showGenText = false;
+ } elseif ($attrs['showGeneratedBy'] === "1") {
+ $wt_report->showGenText = true;
+ }
+ }
+
+ $wt_report->setup();
+ }
+
+ /**
+ * XML </Doc> end element handler
+ */
+ function docEndHandler() {
+ global $wt_report;
+ $wt_report->run();
+ }
+
+ /**
+ * XML <Header> start element handler
+ */
+ function headerStartHandler() {
+ global $wt_report;
+
+ // Clear the Header before any new elements are added
+ $wt_report->clearHeader();
+ $wt_report->setProcessing("H");
+ }
+
+ /**
+ * XML <PageHeader> start element handler
+ */
+ function pageHeaderStartHandler() {
+ global $wt_reportStack, $wt_report, $ReportRoot;
+
+ array_push($this->print_data_stack, $this->print_data);
+ $this->print_data = false;
+ array_push($wt_reportStack, $wt_report);
+ $wt_report = $ReportRoot->createPageHeader();
+ }
+
+ /**
+ * XML <pageHeaderEndHandler> end element handler
+ */
+ function pageHeaderEndHandler() {
+ global $wt_report, $wt_reportStack;
+
+ $this->print_data = array_pop($this->print_data_stack);
+ $this->current_element = $wt_report;
+ $wt_report = array_pop($wt_reportStack);
+ $wt_report->addElement($this->current_element);
+ }
+
+ /**
+ * XML <bodyStartHandler> start element handler
+ */
+ function bodyStartHandler() {
+ global $wt_report;
+ $wt_report->setProcessing("B");
+ }
+
+ /**
+ * XML <footerStartHandler> start element handler
+ */
+ function footerStartHandler() {
+ global $wt_report;
+ $wt_report->setProcessing("F");
+ }
+
+ /**
+ * XML <Cell> start element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function cellStartHandler($attrs) {
+ global $ReportRoot, $wt_report;
+
+ // string The text alignment of the text in this box.
+ $align = "";
+ if (!empty($attrs['align'])) {
+ $align = $attrs['align'];
+ // RTL supported left/right alignment
+ if ($align == "rightrtl") {
+ if ($wt_report->rtl) {
+ $align = "left";
+ } else {
+ $align = "right";
+ }
+ } elseif ($align == "leftrtl") {
+ if ($wt_report->rtl) {
+ $align = "right";
+ } else {
+ $align = "left";
+ }
+ }
+ }
+
+ // string The color to fill the background of this cell
+ $bgcolor = "";
+ if (!empty($attrs['bgcolor'])) {
+ $bgcolor = $attrs['bgcolor'];
+ }
+
+ // int Whether or not the background should be painted
+ $fill = 1;
+ if (isset($attrs['fill'])) {
+ if ($attrs['fill'] === "0") {
+ $fill = 0;
+ } elseif ($attrs['fill'] === "1") {
+ $fill = 1;
+ }
+ }
+
+ $reseth = true;
+ // boolean if true reset the last cell height (default true)
+ if (isset($attrs['reseth'])) {
+ if ($attrs['reseth'] === "0") {
+ $reseth = false;
+ } elseif ($attrs['reseth'] === "1") {
+ $reseth = true;
+ }
+ }
+
+ // mixed Whether or not a border should be printed around this box
+ $border = 0;
+ if (!empty($attrs['border'])) {
+ $border = $attrs['border'];
+ }
+ // string Border color in HTML code
+ $bocolor = "";
+ if (!empty($attrs['bocolor'])) {
+ $bocolor = $attrs['bocolor'];
+ }
+
+ // int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
+ $height = 0;
+ if (!empty($attrs['height'])) {
+ $height = (int) $attrs['height'];
+ }
+ // int Cell width (expressed in points) Setting the width to 0 will make it the width from the current location to the right margin.
+ $width = 0;
+ if (!empty($attrs['width'])) {
+ $width = (int) $attrs['width'];
+ }
+
+ // int Stretch carachter mode
+ $stretch = 0;
+ if (!empty($attrs['stretch'])) {
+ $stretch = (int) $attrs['stretch'];
+ }
+
+ // mixed Position the left corner of this box on the page. The default is the current position.
+ $left = ".";
+ if (isset($attrs['left'])) {
+ if ($attrs['left'] === ".") {
+ $left = ".";
+ } elseif (!empty($attrs['left'])) {
+ $left = (int) $attrs['left'];
+ } elseif ($attrs['left'] === "0") {
+ $left = 0;
+ }
+ }
+ // mixed Position the top corner of this box on the page. the default is the current position
+ $top = ".";
+ if (isset($attrs['top'])) {
+ if ($attrs['top'] === ".") {
+ $top = ".";
+ } elseif (!empty($attrs['top'])) {
+ $top = (int) $attrs['top'];
+ } elseif ($attrs['top'] === "0") {
+ $top = 0;
+ }
+ }
+
+ // string The name of the Style that should be used to render the text.
+ $style = "";
+ if (!empty($attrs['style'])) {
+ $style = $attrs['style'];
+ }
+
+ // string Text color in html code
+ $tcolor = "";
+ if (!empty($attrs['tcolor'])) {
+ $tcolor = $attrs['tcolor'];
+ }
+
+ // int Indicates where the current position should go after the call.
+ $ln = 0;
+ if (isset($attrs['newline'])) {
+ if (!empty($attrs['newline'])) {
+ $ln = (int) $attrs['newline'];
+ } elseif ($attrs['newline'] === "0") {
+ $ln = 0;
+ }
+ }
+
+ if ($align == "left") {
+ $align = "L";
+ } elseif ($align == "right") {
+ $align = "R";
+ } elseif ($align == "center") {
+ $align = "C";
+ } elseif ($align == "justify") {
+ $align = "J";
+ }
+
+ array_push($this->print_data_stack, $this->print_data);
+ $this->print_data = true;
+
+ $this->current_element = $ReportRoot->createCell(
+ $width,
+ $height,
+ $border,
+ $align,
+ $bgcolor,
+ $style,
+ $ln,
+ $top,
+ $left,
+ $fill,
+ $stretch,
+ $bocolor,
+ $tcolor,
+ $reseth
+ );
+ }
+
+ /**
+ * XML </Cell> end element handler
+ */
+ function cellEndHandler() {
+ global $wt_report;
+
+ $this->print_data = array_pop($this->print_data_stack);
+ $wt_report->addElement($this->current_element);
+ }
+
+ /**
+ * XML <Now /> element handler
+ */
+ function nowStartHandler() {
+ $g = timestamp_to_gedcom_date(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
+ $this->current_element->addText($g->display());
+ }
+
+ /**
+ * XML <PageNum /> element handler
+ */
+ function pageNumStartHandler() {
+ $this->current_element->addText("#PAGENUM#");
+ }
+
+ /**
+ * XML <TotalPages /> element handler
+ */
+ function totalPagesStartHandler() {
+ $this->current_element->addText("{{:ptp:}}");
+ }
+
+ /**
+ * Called at the start of an element.
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function gedcomStartHandler($attrs) {
+ global $vars, $WT_TREE;
+
+ if ($this->process_gedcoms > 0) {
+ $this->process_gedcoms++;
+
+ return;
+ }
+
+ $tag = $attrs['id'];
+ $tag = str_replace("@fact", $this->fact, $tag);
+ $tags = explode(":", $tag);
+ $newgedrec = '';
+ if (count($tags) < 2) {
+ $tmp = GedcomRecord::getInstance($attrs['id'], $WT_TREE);
+ $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
+ }
+ if (empty($newgedrec)) {
+ $tgedrec = $this->gedrec;
+ $newgedrec = '';
+ foreach ($tags as $tag) {
+ if (preg_match("/\\$(.+)/", $tag, $match)) {
+ if (isset($vars[$match[1]]['gedcom'])) {
+ $newgedrec = $vars[$match[1]]['gedcom'];
+ } else {
+ $tmp = GedcomRecord::getInstance($match[1], $WT_TREE);
+ $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
+ }
+ } else {
+ if (preg_match("/@(.+)/", $tag, $match)) {
+ $gmatch = array();
+ if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) {
+ $tmp = GedcomRecord::getInstance($gmatch[1], $WT_TREE);
+ $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
+ $tgedrec = $newgedrec;
+ } else {
+ $newgedrec = '';
+ break;
+ }
+ } else {
+ $temp = explode(" ", trim($tgedrec));
+ $level = $temp[0] + 1;
+ $newgedrec = get_sub_record($level, "$level $tag", $tgedrec);
+ $tgedrec = $newgedrec;
+ }
+ }
+ }
+ }
+ if (!empty($newgedrec)) {
+ array_push($this->gedrec_stack, array($this->gedrec, $this->fact, $this->desc));
+ $this->gedrec = $newgedrec;
+ if (preg_match("/(\d+) (_?[A-Z0-9]+) (.*)/", $this->gedrec, $match)) {
+ $this->fact = $match[2];
+ $this->desc = trim($match[3]);
+ }
+ } else {
+ $this->process_gedcoms++;
+ }
+ }
+
+ /**
+ * Called at the end of an element.
+ */
+ function gedcomEndHandler() {
+ if ($this->process_gedcoms > 0) {
+ $this->process_gedcoms--;
+ } else {
+ list($this->gedrec, $this->fact, $this->desc) = array_pop($this->gedrec_stack);
+ }
+ }
+
+ /**
+ * XML <textBoxStartHandler> start element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function textBoxStartHandler($attrs) {
+ global $wt_report, $wt_reportStack, $ReportRoot;
+
+ // string Background color code
+ $bgcolor = "";
+ if (!empty($attrs['bgcolor'])) {
+ $bgcolor = $attrs['bgcolor'];
+ }
+
+ // boolean Wether or not fill the background color
+ $fill = true;
+ if (isset($attrs['fill'])) {
+ if ($attrs['fill'] === "0") {
+ $fill = false;
+ } elseif ($attrs['fill'] === "1") {
+ $fill = true;
+ }
+ }
+
+ // var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
+ $border = false;
+ if (isset($attrs['border'])) {
+ if ($attrs['border'] === "1") {
+ $border = true;
+ } elseif ($attrs['border'] === "0") {
+ $border = false;
+ }
+ }
+
+ // int The starting height of this cell. If the text wraps the height will automatically be adjusted
+ $height = 0;
+ if (!empty($attrs['height'])) {
+ $height = (int) $attrs['height'];
+ }
+ // int Setting the width to 0 will make it the width from the current location to the margin
+ $width = 0;
+ if (!empty($attrs['width'])) {
+ $width = (int) $attrs['width'];
+ }
+
+ // mixed Position the left corner of this box on the page. The default is the current position.
+ $left = ".";
+ if (isset($attrs['left'])) {
+ if ($attrs['left'] === ".") {
+ $left = ".";
+ } elseif (!empty($attrs['left'])) {
+ $left = (int) $attrs['left'];
+ } elseif ($attrs['left'] === "0") {
+ $left = 0;
+ }
+ }
+ // mixed Position the top corner of this box on the page. the default is the current position
+ $top = ".";
+ if (isset($attrs['top'])) {
+ if ($attrs['top'] === ".") {
+ $top = ".";
+ } elseif (!empty($attrs['top'])) {
+ $top = (int) $attrs['top'];
+ } elseif ($attrs['top'] === "0") {
+ $top = 0;
+ }
+ }
+ // boolean After this box is finished rendering, should the next section of text start immediately after the this box or should it start on a new line under this box. 0 = no new line, 1 = force new line. Default is 0
+ $newline = false;
+ if (isset($attrs['newline'])) {
+ if ($attrs['newline'] === "1") {
+ $newline = true;
+ } elseif ($attrs['newline'] === "0") {
+ $newline = false;
+ }
+ }
+ // boolean
+ $pagecheck = true;
+ if (isset($attrs['pagecheck'])) {
+ if ($attrs['pagecheck'] === "0") {
+ $pagecheck = false;
+ } elseif ($attrs['pagecheck'] === "1") {
+ $pagecheck = true;
+ }
+ }
+ // boolean Cell padding
+ $padding = true;
+ if (isset($attrs['padding'])) {
+ if ($attrs['padding'] === "0") {
+ $padding = false;
+ } elseif ($attrs['padding'] === "1") {
+ $padding = true;
+ }
+ }
+ // boolean Reset this box Height
+ $reseth = false;
+ if (isset($attrs['reseth'])) {
+ if ($attrs['reseth'] === "1") {
+ $reseth = true;
+ } elseif ($attrs['reseth'] === "0") {
+ $reseth = false;
+ }
+ }
+
+ // string Style of rendering
+ $style = "";
+
+ array_push($this->print_data_stack, $this->print_data);
+ $this->print_data = false;
+
+ array_push($wt_reportStack, $wt_report);
+ $wt_report = $ReportRoot->createTextBox(
+ $width,
+ $height,
+ $border,
+ $bgcolor,
+ $newline,
+ $left,
+ $top,
+ $pagecheck,
+ $style,
+ $fill,
+ $padding,
+ $reseth
+ );
+ }
+
+ /**
+ * XML <textBoxEndHandler> end element handler
+ */
+ function textBoxEndHandler() {
+ global $wt_report, $wt_reportStack;
+
+ $this->print_data = array_pop($this->print_data_stack);
+ $this->current_element = $wt_report;
+ $wt_report = array_pop($wt_reportStack);
+ $wt_report->addElement($this->current_element);
+ }
+
+ /**
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function textStartHandler($attrs) {
+ global $ReportRoot;
+
+ array_push($this->print_data_stack, $this->print_data);
+ $this->print_data = true;
+
+ // string The name of the Style that should be used to render the text.
+ $style = "";
+ if (!empty($attrs['style'])) {
+ $style = $attrs['style'];
+ }
+
+ // string The color of the text - Keep the black color as default
+ $color = "";
+ if (!empty($attrs['color'])) {
+ $color = $attrs['color'];
+ }
+
+ $this->current_element = $ReportRoot->createText($style, $color);
+ }
+
+ /**
+ *
+ */
+ function textEndHandler() {
+ global $wt_report;
+
+ $this->print_data = array_pop($this->print_data_stack);
+ $wt_report->addElement($this->current_element);
+ }
+
+ /**
+ * XML <GetPersonName> start element handler
+ * Get the name
+ * 1. id is empty - current GEDCOM record
+ * 2. id is set with a record id
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function getPersonNameStartHandler($attrs) {
+ global $vars, $WT_TREE;
+
+ $id = "";
+ $match = array();
+ if (empty($attrs['id'])) {
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $id = $match[1];
+ }
+ } else {
+ if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
+ if (isset($vars[$match[1]]['id'])) {
+ $id = $vars[$match[1]]['id'];
+ }
+ } else {
+ if (preg_match("/@(.+)/", $attrs['id'], $match)) {
+ $gmatch = array();
+ if (preg_match("/\d $match[1] @([^@]+)@/", $this->gedrec, $gmatch)) {
+ $id = $gmatch[1];
+ }
+ } else {
+ $id = $attrs['id'];
+ }
+ }
+ }
+ if (!empty($id)) {
+ $record = GedcomRecord::getInstance($id, $WT_TREE);
+ if (is_null($record)) {
+ return;
+ }
+ if (!$record->canShowName()) {
+ $this->current_element->addText(I18N::translate('Private'));
+ } else {
+ $name = $record->getFullName();
+ $name = preg_replace(
+ array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
+ array('«', '', '»'),
+ $name
+ );
+ $name = strip_tags($name);
+ if (!empty($attrs['truncate'])) {
+ if (mb_strlen($name) > $attrs['truncate']) {
+ $name = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
+ $words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
+ $name = $words[count($words) - 1];
+ for ($i = count($words) - 2; $i >= 0; $i--) {
+ $len = mb_strlen($name);
+ for ($j = count($words) - 3; $j >= 0; $j--) {
+ $len += mb_strlen($words[$j]);
+ }
+ if ($len > $attrs['truncate']) {
+ $first_letter = mb_substr($words[$i], 0, 1);
+ // Do not show " of nick-names
+ if ($first_letter != "\"") {
+ $name = mb_substr($words[$i], 0, 1) . '. ' . $name;
+ }
+ } else {
+ $name = $words[$i] . ' ' . $name;
+ }
+ }
+ }
+ } else {
+ $addname = $record->getAddName();
+ $addname = preg_replace(
+ array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
+ array('«', '', '»'),
+ $addname
+ );
+ $addname = strip_tags($addname);
+ if (!empty($addname)) {
+ $name .= " " . $addname;
+ }
+ }
+ $this->current_element->addText(trim($name));
+ }
+ }
+ }
+
+ /**
+ * XML <GedcomValue> start element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function gedcomValueStartHandler($attrs) {
+ global $WT_TREE;
+
+ $id = "";
+ $match = array();
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $id = $match[1];
+ }
+
+ if (isset($attrs['newline']) && $attrs['newline'] == "1") {
+ $useBreak = "1";
+ } else {
+ $useBreak = "0";
+ }
+
+ $tag = $attrs['tag'];
+ if (!empty($tag)) {
+ if ($tag == "@desc") {
+ $value = $this->desc;
+ $value = trim($value);
+ $this->current_element->addText($value);
+ }
+ if ($tag == "@id") {
+ $this->current_element->addText($id);
+ } else {
+ $tag = str_replace("@fact", $this->fact, $tag);
+ if (empty($attrs['level'])) {
+ $temp = explode(" ", trim($this->gedrec));
+ $level = $temp[0];
+ if ($level == 0) {
+ $level++;
+ }
+ } else {
+ $level = $attrs['level'];
+ }
+ $tags = preg_split('/[: ]/', $tag);
+ $value = get_gedcom_value($tag, $level, $this->gedrec);
+ switch (end($tags)) {
+ case 'DATE':
+ $tmp = new Date($value);
+ $value = $tmp->display();
+ break;
+ case 'PLAC':
+ $tmp = new Place($value, $WT_TREE);
+ $value = $tmp->getShortName();
+ break;
+ }
+ if ($useBreak == "1") {
+ // Insert <br> when multiple dates exist.
+ // This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
+ $value = str_replace('(', '<br>(', $value);
+ $value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
+ $value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
+ if (substr($value, 0, 6) == '<br>') {
+ $value = substr($value, 6);
+ }
+ }
+ $this->current_element->addText($value);
+ }
+ }
+ }
+
+ /**
+ * XML <RepeatTag> start element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function repeatTagStartHandler($attrs) {
+ global $parser;
+
+ $this->process_repeats++;
+ if ($this->process_repeats > 1) {
+ return;
+ }
+
+ array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
+ $this->repeats = array();
+ $this->repeat_bytes = xml_get_current_line_number($parser);
+
+ $tag = "";
+ if (isset($attrs['tag'])) {
+ $tag = $attrs['tag'];
+ }
+ if (!empty($tag)) {
+ if ($tag == "@desc") {
+ $value = $this->desc;
+ $value = trim($value);
+ $this->current_element->addText($value);
+ } else {
+ $tag = str_replace("@fact", $this->fact, $tag);
+ $tags = explode(":", $tag);
+ $temp = explode(" ", trim($this->gedrec));
+ $level = $temp[0];
+ if ($level == 0) {
+ $level++;
+ }
+ $subrec = $this->gedrec;
+ $t = $tag;
+ $count = count($tags);
+ $i = 0;
+ while ($i < $count) {
+ $t = $tags[$i];
+ if (!empty($t)) {
+ if ($i < ($count - 1)) {
+ $subrec = get_sub_record($level, "$level $t", $subrec);
+ if (empty($subrec)) {
+ $level--;
+ $subrec = get_sub_record($level, "@ $t", $this->gedrec);
+ if (empty($subrec)) {
+ return;
+ }
+ }
+ }
+ $level++;
+ }
+ $i++;
+ }
+ $level--;
+ $count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
+ $i = 0;
+ while ($i < $count) {
+ $this->repeats[] = get_sub_record($level, "$level $t", $subrec, $i + 1);
+ $i++;
+ }
+ }
+ }
+ }
+
+ /**
+ * XML </ RepeatTag> end element handler
+ */
+ function repeatTagEndHandler() {
+ global $parser, $parserStack, $report;
+
+ $this->process_repeats--;
+ if ($this->process_repeats > 0) {
+ return;
+ }
+
+ // Check if there is anything to repeat
+ if (count($this->repeats) > 0) {
+ // No need to load them if not used...
+
+ $lineoffset = 0;
+ foreach ($this->repeats_stack as $rep) {
+ $lineoffset += $rep[1];
+ }
+ //-- read the xml from the file
+ $lines = file($report);
+ while (strpos($lines[$lineoffset + $this->repeat_bytes], "<RepeatTag") === false) {
+ $lineoffset--;
+ }
+ $lineoffset++;
+ $reportxml = "<tempdoc>\n";
+ $line_nr = $lineoffset + $this->repeat_bytes;
+ // RepeatTag Level counter
+ $count = 1;
+ while (0 < $count) {
+ if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
+ $count++;
+ } elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
+ $count--;
+ }
+ if (0 < $count) {
+ $reportxml .= $lines[$line_nr];
+ }
+ $line_nr++;
+ }
+ // No need to drag this
+ unset($lines);
+ $reportxml .= "</tempdoc>\n";
+ // Save original values
+ array_push($parserStack, $parser);
+ $oldgedrec = $this->gedrec;
+ foreach ($this->repeats as $gedrec) {
+ $this->gedrec = $gedrec;
+ $repeat_parser = xml_parser_create();
+ $parser = $repeat_parser;
+ xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
+ xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
+ xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
+ if (!xml_parse($repeat_parser, $reportxml, true)) {
+ throw new \DomainException(sprintf(
+ 'RepeatTagEHandler XML error: %s at line %d',
+ xml_error_string(xml_get_error_code($repeat_parser)),
+ xml_get_current_line_number($repeat_parser)
+ ));
+ }
+ xml_parser_free($repeat_parser);
+ }
+ // Restore original values
+ $this->gedrec = $oldgedrec;
+ $parser = array_pop($parserStack);
+ }
+ list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
+ }
+
+ /**
+ * Variable lookup
+ *
+ * Retrieve predefined variables :
+ *
+ * @ desc GEDCOM fact description, example:
+ * 1 EVEN This is a description
+ * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
+ * $ I18N::translate('....')
+ * $ language_settings[]
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function varStartHandler($attrs) {
+ global $parser, $vars;
+
+ if (empty($attrs['var'])) {
+ throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($parser));
+ }
+
+ $var = $attrs['var'];
+ // SetVar element preset variables
+ if (!empty($vars[$var]['id'])) {
+ $var = $vars[$var]['id'];
+ } else {
+ $tfact = $this->fact;
+ if (($this->fact === "EVEN" || $this->fact === "FACT") && $this->type !== " ") {
+ // Use :
+ // n TYPE This text if string
+ $tfact = $this->type;
+ }
+ $var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $this->desc), $var);
+ if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
+ $var = I18N::number($match[1]);
+ } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
+ $var = I18N::translate($match[1]);
+ } elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
+ $var = I18N::translateContext($match[1], $match[2]);
+ }
+ }
+ // Check if variable is set as a date and reformat the date
+ if (isset($attrs['date'])) {
+ if ($attrs['date'] === "1") {
+ $g = new Date($var);
+ $var = $g->display();
+ }
+ }
+ $this->current_element->addText($var);
+ }
+
+ /**
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function factsStartHandler($attrs) {
+ global $parser, $vars, $WT_TREE;
+
+ $this->process_repeats++;
+ if ($this->process_repeats > 1) {
+ return;
+ }
+
+ array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
+ $this->repeats = array();
+ $this->repeat_bytes = xml_get_current_line_number($parser);
+
+ $id = "";
+ $match = array();
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $id = $match[1];
+ }
+ $tag = "";
+ if (isset($attrs['ignore'])) {
+ $tag .= $attrs['ignore'];
+ }
+ if (preg_match("/\\$(.+)/", $tag, $match)) {
+ $tag = $vars[$match[1]]['id'];
+ }
+
+ $record = GedcomRecord::getInstance($id, $WT_TREE);
+ if (empty($attrs['diff']) && !empty($id)) {
+ $facts = $record->getFacts();
+ sort_facts($facts);
+ $this->repeats = array();
+ $nonfacts = explode(',', $tag);
+ foreach ($facts as $event) {
+ if (!in_array($event->getTag(), $nonfacts)) {
+ $this->repeats[] = $event->getGedcom();
+ }
+ }
+ } else {
+ foreach ($record->getFacts() as $fact) {
+ if ($fact->isPendingAddition() && $fact->getTag() !== 'CHAN') {
+ $this->repeats[] = $fact->getGedcom();
+ }
+ }
+ }
+ }
+
+ /**
+ * XML </ Facts> end element handler
+ */
+ function factsEndHandler() {
+ global $parser, $parserStack, $report;
+
+ $this->process_repeats--;
+ if ($this->process_repeats > 0) {
+ return;
+ }
+
+ // Check if there is anything to repeat
+ if (count($this->repeats) > 0) {
+
+ $line = xml_get_current_line_number($parser) - 1;
+ $lineoffset = 0;
+ foreach ($this->repeats_stack as $rep) {
+ $lineoffset += $rep[1];
+ }
+
+ //-- read the xml from the file
+ $lines = file($report);
+ while ($lineoffset + $this->repeat_bytes > 0 && strpos($lines[$lineoffset + $this->repeat_bytes], '<Facts ') === false) {
+ $lineoffset--;
+ }
+ $lineoffset++;
+ $reportxml = "<tempdoc>\n";
+ $i = $line + $lineoffset;
+ $line_nr = $this->repeat_bytes + $lineoffset;
+ while ($line_nr < $i) {
+ $reportxml .= $lines[$line_nr];
+ $line_nr++;
+ }
+ // No need to drag this
+ unset($lines);
+ $reportxml .= "</tempdoc>\n";
+ // Save original values
+ array_push($parserStack, $parser);
+ $oldgedrec = $this->gedrec;
+ $count = count($this->repeats);
+ $i = 0;
+ while ($i < $count) {
+ $this->gedrec = $this->repeats[$i];
+ $this->fact = '';
+ $this->desc = '';
+ if (preg_match('/1 (\w+)(.*)/', $this->gedrec, $match)) {
+ $this->fact = $match[1];
+ if ($this->fact === 'EVEN' || $this->fact === 'FACT') {
+ $tmatch = array();
+ if (preg_match('/2 TYPE (.+)/', $this->gedrec, $tmatch)) {
+ $this->type = trim($tmatch[1]);
+ } else {
+ $this->type = ' ';
+ }
+ }
+ $this->desc = trim($match[2]);
+ $this->desc .= get_cont(2, $this->gedrec);
+ }
+ $repeat_parser = xml_parser_create();
+ $parser = $repeat_parser;
+ xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
+ xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
+ xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
+ if (!xml_parse($repeat_parser, $reportxml, true)) {
+ throw new \DomainException(sprintf(
+ 'FactsEHandler XML error: %s at line %d',
+ xml_error_string(xml_get_error_code($repeat_parser)),
+ xml_get_current_line_number($repeat_parser)
+ ));
+ }
+ xml_parser_free($repeat_parser);
+ $i++;
+ }
+ // Restore original values
+ $parser = array_pop($parserStack);
+ $this->gedrec = $oldgedrec;
+ }
+ list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
+ }
+
+ /**
+ * Setting upp or changing variables in the XML
+ * The XML variable name and value is stored in the global variable $vars
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function setVarStartHandler($attrs) {
+ global $vars;
+
+ if (empty($attrs['name'])) {
+ throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
+ }
+
+ $name = $attrs['name'];
+ $value = $attrs['value'];
+ $match = array();
+ // Current GEDCOM record strings
+ if ($value == "@ID") {
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $value = $match[1];
+ }
+ } elseif ($value == "@fact") {
+ $value = $this->fact;
+ } elseif ($value == "@desc") {
+ $value = $this->desc;
+ } elseif ($value == "@generation") {
+ $value = $this->generation;
+ } elseif (preg_match("/@(\w+)/", $value, $match)) {
+ $gmatch = array();
+ if (preg_match("/\d $match[1] (.+)/", $this->gedrec, $gmatch)) {
+ $value = str_replace("@", "", trim($gmatch[1]));
+ }
+ }
+ if (preg_match("/\\$(\w+)/", $name, $match)) {
+ $name = $vars["'" . $match[1] . "'"]['id'];
+ }
+ $count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
+ $i = 0;
+ while ($i < $count) {
+ $t = $vars[$match[$i][1]]['id'];
+ $value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
+ $i++;
+ }
+ if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
+ $value = I18N::number($match[1]);
+ } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
+ $value = I18N::translate($match[1]);
+ } elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
+ $value = I18N::translateContext($match[1], $match[2]);
+ }
+ // Arithmetic functions
+ if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
+ switch ($match[2]) {
+ case "+":
+ $t = $match[1] + $match[3];
+ $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
+ break;
+ case "-":
+ $t = $match[1] - $match[3];
+ $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
+ break;
+ case "*":
+ $t = $match[1] * $match[3];
+ $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
+ break;
+ case "/":
+ $t = $match[1] / $match[3];
+ $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
+ break;
+ }
+ }
+ if (strpos($value, "@") !== false) {
+ $value = "";
+ }
+ $vars[$name]['id'] = $value;
+ }
+
+ /**
+ * XML <if > start element
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function ifStartHandler($attrs) {
+ global $vars;
+
+ if ($this->process_ifs > 0) {
+ $this->process_ifs++;
+
+ return;
+ }
+
+ $condition = $attrs['condition'];
+ $condition = preg_replace("/\\$(\w+)/", "\$vars[\"$1\"][\"id\"]", $condition);
+ $condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
+ // Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
+ $condition = str_replace("@fact", $this->fact, $condition);
+ $match = array();
+ $count = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
+ $i = 0;
+ while ($i < $count) {
+ $id = $match[$i][1];
+ $value = '""';
+ if ($id == "ID") {
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $value = "'" . $match[1] . "'";
+ }
+ } elseif ($id === "fact") {
+ $value = '"' . $this->fact . '"';
+ } elseif ($id === "desc") {
+ $value = '"' . addslashes($this->desc) . '"';
+ } elseif ($id === "generation") {
+ $value = '"' . $this->generation . '"';
+ } else {
+
+ $temp = explode(" ", trim($this->gedrec));
+ $level = $temp[0];
+ if ($level == 0) {
+ $level++;
+ }
+ $value = get_gedcom_value($id, $level, $this->gedrec);
+ if (empty($value)) {
+ $level++;
+ $value = get_gedcom_value($id, $level, $this->gedrec);
+ }
+ $value = preg_replace("/^@(" . WT_REGEX_XREF . ")@$/", "$1", $value);
+ $value = "\"" . addslashes($value) . "\"";
+ }
+ $condition = str_replace("@$id", $value, $condition);
+ $i++;
+ }
+ $condition = "return (bool) ($condition);";
+ $ret = @eval($condition);
+ if (!$ret) {
+ $this->process_ifs++;
+ }
+ }
+
+ /**
+ * XML <if /> end element
+ */
+ function ifEndHandler() {
+ if ($this->process_ifs > 0) {
+ $this->process_ifs--;
+ }
+ }
+
+ /**
+ * XML <Footnote > start element
+ * Collect the Footnote links
+ * GEDCOM Records that are protected by Privacy setting will be ignore
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function footnoteStartHandler($attrs) {
+ global $ReportRoot, $WT_TREE;
+
+ $id = "";
+ if (preg_match("/[0-9] (.+) @(.+)@/", $this->gedrec, $match)) {
+ $id = $match[2];
+ }
+ $record = GedcomRecord::GetInstance($id, $WT_TREE);
+ if ($record && $record->canShow()) {
+ array_push($this->print_data_stack, $this->print_data);
+ $this->print_data = true;
+ $style = "";
+ if (!empty($attrs['style'])) {
+ $style = $attrs['style'];
+ }
+ $this->footnote_element = $this->current_element;
+ $this->current_element = $ReportRoot->createFootnote($style);
+ } else {
+ $this->print_data = false;
+ $this->process_footnote = false;
+ }
+ }
+
+ /**
+ * XML <Footnote /> end element
+ * Print the collected Footnote data
+ */
+ function footnoteEndHandler() {
+ global $wt_report;
+
+ if ($this->process_footnote) {
+ $this->print_data = array_pop($this->print_data_stack);
+ $temp = trim($this->current_element->getValue());
+ if (strlen($temp) > 3) {
+ $wt_report->addElement($this->current_element);
+ }
+ $this->current_element = $this->footnote_element;
+ } else {
+ $this->process_footnote = true;
+ }
+ }
+
+ /**
+ * XML <FootnoteTexts /> element
+ */
+ function footnoteTextsStartHandler() {
+ global $wt_report;
+
+ $temp = "footnotetexts";
+ $wt_report->addElement($temp);
+ }
+
+ /**
+ * XML <AgeAtDeath /> element handler
+ */
+ function ageAtDeathStartHandler() {
+ // This duplicates functionality in format_fact_date()
+ global $factrec, $WT_TREE;
+
+ $match = array();
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $person = Individual::getInstance($match[1], $WT_TREE);
+ // Recorded age
+ if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
+ $fact_age = $match[1];
+ } else {
+ $fact_age = '';
+ }
+ if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
+ $husb_age = $match[1];
+ } else {
+ $husb_age = '';
+ }
+ if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
+ $wife_age = $match[1];
+ } else {
+ $wife_age = '';
+ }
+
+ // Calculated age
+ $birth_date = $person->getBirthDate();
+ // Can't use getDeathDate(), as this also gives BURI/CREM events, which
+ // wouldn't give the correct "days after death" result for people with
+ // no DEAT.
+ $death_event = $person->getFirstFact('DEAT');
+ if ($death_event) {
+ $death_date = $death_event->getDate();
+ } else {
+ $death_date = new Date('');
+ }
+ $value = '';
+ if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
+ $age = Date::getAgeGedcom($birth_date, $death_date);
+ // Only show calculated age if it differs from recorded age
+ if ($age != '' && $age != "0d") {
+ if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
+ ) {
+ $value = get_age_at_event($age, false);
+ $abbrev = substr($value, 0, strpos($value, ' ') + 5);
+ if ($value !== $abbrev) {
+ $value = $abbrev . '.';
+ }
+ }
+ }
+ }
+ $this->current_element->addText($value);
+ }
+ }
+
+ /**
+ * XML element Forced line break handler - HTML code
+ */
+ function brStartHandler() {
+ if ($this->print_data && $this->process_gedcoms === 0) {
+ $this->current_element->addText('<br>');
+ }
+ }
+
+ /**
+ * XML <sp />element Forced space handler
+ */
+ function spStartHandler() {
+ if ($this->print_data && $this->process_gedcoms === 0) {
+ $this->current_element->addText(' ');
+ }
+ }
+
+ /**
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function highlightedImageStartHandler($attrs) {
+ global $wt_report, $ReportRoot, $WT_TREE;
+
+ $id = '';
+ $match = array();
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $id = $match[1];
+ }
+
+ // mixed Position the top corner of this box on the page. the default is the current position
+ $top = '.';
+ if (isset($attrs['top'])) {
+ if ($attrs['top'] === '0') {
+ $top = 0;
+ } elseif ($attrs['top'] === '.') {
+ $top = '.';
+ } elseif (!empty($attrs['top'])) {
+ $top = (int) $attrs['top'];
+ }
+ }
+
+ // mixed Position the left corner of this box on the page. the default is the current position
+ $left = '.';
+ if (isset($attrs['left'])) {
+ if ($attrs['left'] === '0') {
+ $left = 0;
+ } elseif ($attrs['left'] === '.') {
+ $left = '.';
+ } elseif (!empty($attrs['left'])) {
+ $left = (int) $attrs['left'];
+ }
+ }
+
+ // string Align the image in left, center, right
+ $align = '';
+ if (!empty($attrs['align'])) {
+ $align = $attrs['align'];
+ }
+
+ // string Next Line should be T:next to the image, N:next line
+ $ln = '';
+ if (!empty($attrs['ln'])) {
+ $ln = $attrs['ln'];
+ }
+
+ $width = 0;
+ $height = 0;
+ if (!empty($attrs['width'])) {
+ $width = (int) $attrs['width'];
+ }
+ if (!empty($attrs['height'])) {
+ $height = (int) $attrs['height'];
+ }
+
+ $person = Individual::getInstance($id, $WT_TREE);
+ $mediaobject = $person->findHighlightedMedia();
+ if ($mediaobject) {
+ $attributes = $mediaobject->getImageAttributes('thumb');
+ if (in_array(
+ $attributes['ext'],
+ array(
+ 'GIF',
+ 'JPG',
+ 'PNG',
+ 'SWF',
+ 'PSD',
+ 'BMP',
+ 'TIFF',
+ 'TIFF',
+ 'JPC',
+ 'JP2',
+ 'JPX',
+ 'JB2',
+ 'SWC',
+ 'IFF',
+ 'WBMP',
+ 'XBM',
+ )
+ ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
+ ) {
+ if ($width > 0 && $height == 0) {
+ $perc = $width / $attributes['adjW'];
+ $height = round($attributes['adjH'] * $perc);
+ } elseif ($height > 0 && $width == 0) {
+ $perc = $height / $attributes['adjH'];
+ $width = round($attributes['adjW'] * $perc);
+ } else {
+ $width = $attributes['adjW'];
+ $height = $attributes['adjH'];
+ }
+ $image = $ReportRoot->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
+ $wt_report->addElement($image);
+ }
+ }
+ }
+
+ /**
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function imageStartHandler($attrs) {
+ global $wt_report, $ReportRoot, $WT_TREE;
+
+ // mixed Position the top corner of this box on the page. the default is the current position
+ $top = '.';
+ if (isset($attrs['top'])) {
+ if ($attrs['top'] === "0") {
+ $top = 0;
+ } elseif ($attrs['top'] === '.') {
+ $top = '.';
+ } elseif (!empty($attrs['top'])) {
+ $top = (int) $attrs['top'];
+ }
+ }
+
+ // mixed Position the left corner of this box on the page. the default is the current position
+ $left = '.';
+ if (isset($attrs['left'])) {
+ if ($attrs['left'] === '0') {
+ $left = 0;
+ } elseif ($attrs['left'] === '.') {
+ $left = '.';
+ } elseif (!empty($attrs['left'])) {
+ $left = (int) $attrs['left'];
+ }
+ }
+
+ // string Align the image in left, center, right
+ $align = '';
+ if (!empty($attrs['align'])) {
+ $align = $attrs['align'];
+ }
+
+ // string Next Line should be T:next to the image, N:next line
+ $ln = 'T';
+ if (!empty($attrs['ln'])) {
+ $ln = $attrs['ln'];
+ }
+
+ $width = 0;
+ $height = 0;
+ if (!empty($attrs['width'])) {
+ $width = (int) $attrs['width'];
+ }
+ if (!empty($attrs['height'])) {
+ $height = (int) $attrs['height'];
+ }
+
+ $file = '';
+ if (!empty($attrs['file'])) {
+ $file = $attrs['file'];
+ }
+ if ($file == "@FILE") {
+ $match = array();
+ if (preg_match("/\d OBJE @(.+)@/", $this->gedrec, $match)) {
+ $mediaobject = Media::getInstance($match[1], $WT_TREE);
+ $attributes = $mediaobject->getImageAttributes('thumb');
+ if (in_array(
+ $attributes['ext'],
+ array(
+ 'GIF',
+ 'JPG',
+ 'PNG',
+ 'SWF',
+ 'PSD',
+ 'BMP',
+ 'TIFF',
+ 'TIFF',
+ 'JPC',
+ 'JP2',
+ 'JPX',
+ 'JB2',
+ 'SWC',
+ 'IFF',
+ 'WBMP',
+ 'XBM',
+ )
+ ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
+ ) {
+ if ($width > 0 && $height == 0) {
+ $perc = $width / $attributes['adjW'];
+ $height = round($attributes['adjH'] * $perc);
+ } elseif ($height > 0 && $width == 0) {
+ $perc = $height / $attributes['adjH'];
+ $width = round($attributes['adjW'] * $perc);
+ } else {
+ $width = $attributes['adjW'];
+ $height = $attributes['adjH'];
+ }
+ $image = $ReportRoot->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
+ $wt_report->addElement($image);
+ }
+ }
+ } else {
+ if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
+ $size = getimagesize($file);
+ if ($width > 0 && $height == 0) {
+ $perc = $width / $size[0];
+ $height = round($size[1] * $perc);
+ } elseif ($height > 0 && $width == 0) {
+ $perc = $height / $size[1];
+ $width = round($size[0] * $perc);
+ } else {
+ $width = $size[0];
+ $height = $size[1];
+ }
+ $image = $ReportRoot->createImage($file, $left, $top, $width, $height, $align, $ln);
+ $wt_report->addElement($image);
+ }
+ }
+ }
+
+ /**
+ * XML <Line> element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function lineStartHandler($attrs) {
+ global $wt_report, $ReportRoot;
+
+ // Start horizontal position, current position (default)
+ $x1 = ".";
+ if (isset($attrs['x1'])) {
+ if ($attrs['x1'] === "0") {
+ $x1 = 0;
+ } elseif ($attrs['x1'] === ".") {
+ $x1 = ".";
+ } elseif (!empty($attrs['x1'])) {
+ $x1 = (int) $attrs['x1'];
+ }
+ }
+ // Start vertical position, current position (default)
+ $y1 = ".";
+ if (isset($attrs['y1'])) {
+ if ($attrs['y1'] === "0") {
+ $y1 = 0;
+ } elseif ($attrs['y1'] === ".") {
+ $y1 = ".";
+ } elseif (!empty($attrs['y1'])) {
+ $y1 = (int) $attrs['y1'];
+ }
+ }
+ // End horizontal position, maximum width (default)
+ $x2 = ".";
+ if (isset($attrs['x2'])) {
+ if ($attrs['x2'] === "0") {
+ $x2 = 0;
+ } elseif ($attrs['x2'] === ".") {
+ $x2 = ".";
+ } elseif (!empty($attrs['x2'])) {
+ $x2 = (int) $attrs['x2'];
+ }
+ }
+ // End vertical position
+ $y2 = ".";
+ if (isset($attrs['y2'])) {
+ if ($attrs['y2'] === "0") {
+ $y2 = 0;
+ } elseif ($attrs['y2'] === ".") {
+ $y2 = ".";
+ } elseif (!empty($attrs['y2'])) {
+ $y2 = (int) $attrs['y2'];
+ }
+ }
+
+ $line = $ReportRoot->createLine($x1, $y1, $x2, $y2);
+ $wt_report->addElement($line);
+ }
+
+ /**
+ * XML <List> start element handler
+ *
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function listStartHandler($attrs) {
+ global $parser, $vars, $WT_TREE;
+
+ $this->process_repeats++;
+ if ($this->process_repeats > 1) {
+ return;
+ }
+
+ $match = array();
+ if (isset($attrs['sortby'])) {
+ $sortby = $attrs['sortby'];
+ if (preg_match("/\\$(\w+)/", $sortby, $match)) {
+ $sortby = $vars[$match[1]]['id'];
+ $sortby = trim($sortby);
+ }
+ } else {
+ $sortby = "NAME";
+ }
+
+ if (isset($attrs['list'])) {
+ $listname = $attrs['list'];
+ } else {
+ $listname = "individual";
+ }
+ // Some filters/sorts can be applied using SQL, while others require PHP
+ switch ($listname) {
+ case "pending":
+ $rows = Database::prepare(
+ "SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" . " FROM `##change`" . " WHERE (xref, change_id) IN (" . " SELECT xref, MAX(change_id)" . " FROM `##change`" . " WHERE status='pending' AND gedcom_id=?" . " GROUP BY xref" . " )"
+ )->execute(array($WT_TREE->getTreeId()))->fetchAll();
+ $this->list = array();
+ foreach ($rows as $row) {
+ $this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
+ }
+ break;
+ case 'individual':
+ $sql_select = "SELECT DISTINCT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
+ $sql_join = "";
+ $sql_where = " WHERE i_file = " . $WT_TREE->getTreeId();
+ $sql_order_by = "";
+ foreach ($attrs as $attr => $value) {
+ if (strpos($attr, 'filter') === 0 && $value) {
+ // Substitute global vars
+ $value = preg_replace_callback(
+ '/\$(\w+)/',
+ function ($matches) use ($vars) {
+ return $vars[$matches[1]]['id'];
+ },
+ $value
+ );
+ // Convert the various filters into SQL
+ if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
+ $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
+ $sql_where .= " AND {$attr}.d_fact='{$match[1]}'";
+ $date = new Date($match[3]);
+ if ($match[2] == "LTE") {
+ $sql_where .= " AND {$attr}.d_julianday2<=" . $date->minimumJulianDay();
+ } else {
+ $sql_where .= " AND {$attr}.d_julianday1>=" . $date->minimumJulianDay();
+ }
+ if ($sortby == $match[1]) {
+ $sortby = "";
+ $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
+ }
+ unset($attrs[$attr]); // This filter has been fully processed
+ } elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
+ // Do nothing, unless you have to
+ if ($match[1] != '' || $sortby == 'NAME') {
+ $sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
+ // Search the DB only if there is any name supplied
+ if ($match[1] != "") {
+ $names = explode(" ", $match[1]);
+ foreach ($names as $name) {
+ $sql_where .= " AND {$attr}.n_full LIKE " . Database::quote("%{$name}%");
+ }
+ }
+ // Let the DB do the name sorting even when no name was entered
+ if ($sortby == "NAME") {
+ $sortby = "";
+ $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
+ }
+ }
+ unset($attrs[$attr]); // This filter has been fully processed
+ } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
+ $sql_where .= " AND i_gedcom REGEXP '" . $match[1] . "'";
+ unset($attrs[$attr]); // This filter has been fully processed
+ } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
+ $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=i_file)";
+ $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=i_id)";
+ $sql_where .= " AND {$attr}a.p_place LIKE " . Database::quote("%{$match[1]}%");
+ // Don't unset this filter. This is just initial filtering
+ } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
+ $query = "";
+ // Level 1 tag
+ if ($match[1] !== '') {
+ $query .= "%1 {$match[1]}%";
+ }
+ // Level 2 tag
+ if ($match[2] !== '') {
+ $query .= "%2 {$match[2]}%";
+ }
+ // Contains what?
+ if ($match[3] !== '') {
+ $query .= "%{$match[3]}%";
+ }
+ $sql_where .= " AND i_gedcom LIKE " . Database::quote($query);
+ // Don't unset this filter. This is just initial filtering
+ }
+ }
+ }
+
+ $this->list = array();
+ $rows = Database::prepare(
+ $sql_select . $sql_join . $sql_where . $sql_order_by
+ )->fetchAll();
+
+ foreach ($rows as $row) {
+ $this->list[] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
+ }
+ break;
+
+ case 'family':
+ $sql_select = "SELECT DISTINCT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
+ $sql_join = "";
+ $sql_where = " WHERE f_file=" . $WT_TREE->getTreeId();
+ $sql_order_by = "";
+ foreach ($attrs as $attr => $value) {
+ if (strpos($attr, 'filter') === 0 && $value) {
+ // Substitute global vars
+ $value = preg_replace_callback(
+ '/\$(\w+)/',
+ function ($matches) use ($vars) {
+ return $vars[$matches[1]]['id'];
+ },
+ $value
+ );
+ // Convert the various filters into SQL
+ if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
+ $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
+ $sql_where .= " AND {$attr}.d_fact='{$match[1]}'";
+ $date = new Date($match[3]);
+ if ($match[2] == "LTE") {
+ $sql_where .= " AND {$attr}.d_julianday2<=" . $date->minimumJulianDay();
+ } else {
+ $sql_where .= " AND {$attr}.d_julianday1>=" . $date->minimumJulianDay();
+ }
+ if ($sortby == $match[1]) {
+ $sortby = "";
+ $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
+ }
+ unset($attrs[$attr]); // This filter has been fully processed
+ } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
+ $sql_where .= " AND f_gedcom REGEXP '" . $match[1] . "'";
+ unset($attrs[$attr]); // This filter has been fully processed
+ } elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
+ $sql_join .= " JOIN `##link` AS {$attr}a ON ({$attr}a.l_file=f_file AND {$attr}a.l_from=f_id)";
+ $sql_join .= " JOIN `##name` AS {$attr}b ON ({$attr}b.n_file=f_file AND n_id=f_id)";
+ $sql_where .= " AND {$attr}a.l_type=IN ('HUSB, 'WIFE')";
+ $sql_where .= " AND {$attr}.n_full LIKE " . Database::quote("%{$match[1]}%");
+ if ($sortby == "NAME") {
+ $sortby = "";
+ $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
+ }
+ unset($attrs[$attr]); // This filter has been fully processed
+ } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
+ $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
+ $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
+ $sql_where .= " AND {$attr}a.p_place LIKE " . Database::quote("%{$match[1]}%");
+ // Don't unset this filter. This is just initial filtering
+ } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
+ $query = "";
+ // Level 1 tag
+ if ($match[1] !== '') {
+ $query .= "%1 {$match[1]}%";
+ }
+ // Level 2 tag
+ if ($match[2] !== '') {
+ $query .= "%2 {$match[2]}%";
+ }
+ // Contains what?
+ if ($match[3] !== '') {
+ $query .= "%{$match[3]}%";
+ }
+ $sql_where .= " AND f_gedcom LIKE " . Database::quote($query);
+ // Don't unset this filter. This is just initial filtering
+ }
+ }
+ }
+
+ $this->list = array();
+ $rows = Database::prepare(
+ $sql_select . $sql_join . $sql_where . $sql_order_by
+ )->fetchAll();
+
+ foreach ($rows as $row) {
+ $this->list[] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
+ }
+ break;
+
+ default:
+ throw new \DomainException('Invalid list name: ' . $listname);
+ }
+
+ $filters = array();
+ $filters2 = array();
+ if (isset($attrs['filter1']) && count($this->list) > 0) {
+ foreach ($attrs as $key => $value) {
+ if (preg_match("/filter(\d)/", $key)) {
+ $condition = $value;
+ if (preg_match("/@(\w+)/", $condition, $match)) {
+ $id = $match[1];
+ $value = "''";
+ if ($id == "ID") {
+ if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
+ $value = "'" . $match[1] . "'";
+ }
+ } elseif ($id == "fact") {
+ $value = "'" . $this->fact . "'";
+ } elseif ($id == "desc") {
+ $value = "'" . $this->desc . "'";
+ } else {
+ if (preg_match("/\d $id (.+)/", $this->gedrec, $match)) {
+ $value = "'" . str_replace("@", "", trim($match[1])) . "'";
+ }
+ }
+ $condition = preg_replace("/@$id/", $value, $condition);
+ }
+ //-- handle regular expressions
+ if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
+ $tag = trim($match[1]);
+ $expr = trim($match[2]);
+ $val = trim($match[3]);
+ if (preg_match("/\\$(\w+)/", $val, $match)) {
+ $val = $vars[$match[1]]['id'];
+ $val = trim($val);
+ }
+ if ($val) {
+ $searchstr = "";
+ $tags = explode(":", $tag);
+ //-- only limit to a level number if we are specifically looking at a level
+ if (count($tags) > 1) {
+ $level = 1;
+ foreach ($tags as $t) {
+ if (!empty($searchstr)) {
+ $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
+ }
+ //-- search for both EMAIL and _EMAIL... silly double gedcom standard
+ if ($t == "EMAIL" || $t == "_EMAIL") {
+ $t = "_?EMAIL";
+ }
+ $searchstr .= $level . " " . $t;
+ $level++;
+ }
+ } else {
+ if ($tag == "EMAIL" || $tag == "_EMAIL") {
+ $tag = "_?EMAIL";
+ }
+ $t = $tag;
+ $searchstr = "1 " . $tag;
+ }
+ switch ($expr) {
+ case "CONTAINS":
+ if ($t == "PLAC") {
+ $searchstr .= "[^\n]*[, ]*" . $val;
+ } else {
+ $searchstr .= "[^\n]*" . $val;
+ }
+ $filters[] = $searchstr;
+ break;
+ default:
+ $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ //-- apply other filters to the list that could not be added to the search string
+ if ($filters) {
+ foreach ($this->list as $key => $record) {
+ foreach ($filters as $filter) {
+ if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
+ unset($this->list[$key]);
+ break;
+ }
+ }
+ }
+ }
+ if ($filters2) {
+ $mylist = array();
+ foreach ($this->list as $indi) {
+ $key = $indi->getXref();
+ $grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
+ $keep = true;
+ foreach ($filters2 as $filter) {
+ if ($keep) {
+ $tag = $filter['tag'];
+ $expr = $filter['expr'];
+ $val = $filter['val'];
+ if ($val == "''") {
+ $val = "";
+ }
+ $tags = explode(":", $tag);
+ $t = end($tags);
+ $v = get_gedcom_value($tag, 1, $grec);
+ //-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
+ if ($t == "EMAIL" && empty($v)) {
+ $tag = str_replace("EMAIL", "_EMAIL", $tag);
+ $tags = explode(":", $tag);
+ $t = end($tags);
+ $v = get_sub_record(1, $tag, $grec);
+ }
+
+ switch ($expr) {
+ case "GTE":
+ if ($t == "DATE") {
+ $date1 = new Date($v);
+ $date2 = new Date($val);
+ $keep = (Date::compare($date1, $date2) >= 0);
+ } elseif ($val >= $v) {
+ $keep = true;
+ }
+ break;
+ case "LTE":
+ if ($t == "DATE") {
+ $date1 = new Date($v);
+ $date2 = new Date($val);
+ $keep = (Date::compare($date1, $date2) <= 0);
+ } elseif ($val >= $v) {
+ $keep = true;
+ }
+ break;
+ default:
+ if ($v == $val) {
+ $keep = true;
+ } else {
+ $keep = false;
+ }
+ break;
+ }
+ }
+ }
+ if ($keep) {
+ $mylist[$key] = $indi;
+ }
+ }
+ $this->list = $mylist;
+ }
+
+ switch ($sortby) {
+ case 'NAME':
+ uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
+ break;
+ case 'CHAN':
+ uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
+ return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
+ });
+ break;
+ case 'BIRT:DATE':
+ uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
+ break;
+ case 'DEAT:DATE':
+ uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
+ break;
+ case 'MARR:DATE':
+ uasort($this->list, '\Fisharebest\Webtrees\\Family::compareMarrDate');
+ break;
+ default:
+ // unsorted or already sorted by SQL
+ break;
+ }
+
+ array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
+ $this->repeat_bytes = xml_get_current_line_number($parser) + 1;
+ }
+
+ /**
+ * XML <List> end element handler
+ */
+ function listEndHandler() {
+ global $parser, $parserStack, $report;
+
+ $this->process_repeats--;
+ if ($this->process_repeats > 0) {
+ return;
+ }
+
+ // Check if there is any list
+ if (count($this->list) > 0) {
+ $lineoffset = 0;
+ foreach ($this->repeats_stack as $rep) {
+ $lineoffset += $rep[1];
+ }
+ //-- read the xml from the file
+ $lines = file($report);
+ while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<List") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
+ $lineoffset--;
+ }
+ $lineoffset++;
+ $reportxml = "<tempdoc>\n";
+ $line_nr = $lineoffset + $this->repeat_bytes;
+ // List Level counter
+ $count = 1;
+ while (0 < $count) {
+ if (strpos($lines[$line_nr], "<List") !== false) {
+ $count++;
+ } elseif (strpos($lines[$line_nr], "</List") !== false) {
+ $count--;
+ }
+ if (0 < $count) {
+ $reportxml .= $lines[$line_nr];
+ }
+ $line_nr++;
+ }
+ // No need to drag this
+ unset($lines);
+ $reportxml .= "</tempdoc>";
+ // Save original values
+ array_push($parserStack, $parser);
+ $oldgedrec = $this->gedrec;
+
+ $this->list_total = count($this->list);
+ $this->list_private = 0;
+ foreach ($this->list as $record) {
+ if ($record->canShow()) {
+ $this->gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
+ //-- start the sax parser
+ $repeat_parser = xml_parser_create();
+ $parser = $repeat_parser;
+ xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
+ xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
+ xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
+ if (!xml_parse($repeat_parser, $reportxml, true)) {
+ throw new \DomainException(sprintf(
+ 'ListEHandler XML error: %s at line %d',
+ xml_error_string(xml_get_error_code($repeat_parser)),
+ xml_get_current_line_number($repeat_parser)
+ ));
+ }
+ xml_parser_free($repeat_parser);
+ } else {
+ $this->list_private++;
+ }
+ }
+ $this->list = array();
+ $parser = array_pop($parserStack);
+ $this->gedrec = $oldgedrec;
+ }
+ list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
+ }
+
+ /**
+ * XML <ListTotal> element handler
+ *
+ * Prints the total number of records in a list
+ * The total number is collected from
+ * List and Relatives
+ */
+ function listTotalStartHandler() {
+ if ($this->list_private == 0) {
+ $this->current_element->addText($this->list_total);
+ } else {
+ $this->current_element->addText(($this->list_total - $this->list_private) . " / " . $this->list_total);
+ }
+ }
+
+ /**
+ * @param array $attrs an array of key value pairs for the attributes
+ */
+ function relativesStartHandler($attrs) {
+ global $parser, $vars, $WT_TREE;
+
+ $this->process_repeats++;
+ if ($this->process_repeats > 1) {
+ return;
+ }
+
+ $sortby = "NAME";
+ if (isset($attrs['sortby'])) {
+ $sortby = $attrs['sortby'];
+ }
+ $match = array();
+ if (preg_match("/\\$(\w+)/", $sortby, $match)) {
+ $sortby = $vars[$match[1]]['id'];
+ $sortby = trim($sortby);
+ }
+
+ $maxgen = -1;
+ if (isset($attrs['maxgen'])) {
+ $maxgen = $attrs['maxgen'];
+ }
+ if ($maxgen == "*") {
+ $maxgen = -1;
+ }
+
+ $group = "child-family";
+ if (isset($attrs['group'])) {
+ $group = $attrs['group'];
+ }
+ if (preg_match("/\\$(\w+)/", $group, $match)) {
+ $group = $vars[$match[1]]['id'];
+ $group = trim($group);
+ }
+
+ $id = "";
+ if (isset($attrs['id'])) {
+ $id = $attrs['id'];
+ }
+ if (preg_match("/\\$(\w+)/", $id, $match)) {
+ $id = $vars[$match[1]]['id'];
+ $id = trim($id);
+ }
+
+ $this->list = array();
+ $person = Individual::getInstance($id, $WT_TREE);
+ if (!empty($person)) {
+ $this->list[$id] = $person;
+ switch ($group) {
+ case "child-family":
+ foreach ($person->getChildFamilies() as $family) {
+ $husband = $family->getHusband();
+ $wife = $family->getWife();
+ if (!empty($husband)) {
+ $this->list[$husband->getXref()] = $husband;
+ }
+ if (!empty($wife)) {
+ $this->list[$wife->getXref()] = $wife;
+ }
+ $children = $family->getChildren();
+ foreach ($children as $child) {
+ if (!empty($child)) {
+ $this->list[$child->getXref()] = $child;
+ }
+ }
+ }
+ break;
+ case "spouse-family":
+ foreach ($person->getSpouseFamilies() as $family) {
+ $husband = $family->getHusband();
+ $wife = $family->getWife();
+ if (!empty($husband)) {
+ $this->list[$husband->getXref()] = $husband;
+ }
+ if (!empty($wife)) {
+ $this->list[$wife->getXref()] = $wife;
+ }
+ $children = $family->getChildren();
+ foreach ($children as $child) {
+ if (!empty($child)) {
+ $this->list[$child->getXref()] = $child;
+ }
+ }
+ }
+ break;
+ case "direct-ancestors":
+ add_ancestors($this->list, $id, false, $maxgen);
+ break;
+ case "ancestors":
+ add_ancestors($this->list, $id, true, $maxgen);
+ break;
+ case "descendants":
+ $this->list[$id]->generation = 1;
+ add_descendancy($this->list, $id, false, $maxgen);
+ break;
+ case "all":
+ add_ancestors($this->list, $id, true, $maxgen);
+ add_descendancy($this->list, $id, true, $maxgen);
+ break;
+ }
+ }
+
+ switch ($sortby) {
+ case 'NAME':
+ uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
+ break;
+ case 'BIRT:DATE':
+ uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
+ break;
+ case 'DEAT:DATE':
+ uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
+ break;
+ case 'generation':
+ $newarray = array();
+ reset($this->list);
+ $genCounter = 1;
+ while (count($newarray) < count($this->list)) {
+ foreach ($this->list as $key => $value) {
+ $this->generation = $value->generation;
+ if ($this->generation == $genCounter) {
+ $newarray[$key] = new \stdClass;
+ $newarray[$key]->generation = $this->generation;
+ }
+ }
+ $genCounter++;
+ }
+ $this->list = $newarray;
+ break;
+ default:
+ // unsorted
+ break;
+ }
+ array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
+ $this->repeat_bytes = xml_get_current_line_number($parser) + 1;
+ }
+
+ /**
+ * XML </ Relatives> end element handler
+ */
+ function relativesEndHandler() {
+ global $parser, $parserStack, $report, $WT_TREE;
+
+ $this->process_repeats--;
+ if ($this->process_repeats > 0) {
+ return;
+ }
+
+ // Check if there is any relatives
+ if (count($this->list) > 0) {
+ $lineoffset = 0;
+ foreach ($this->repeats_stack as $rep) {
+ $lineoffset += $rep[1];
+ }
+ //-- read the xml from the file
+ $lines = file($report);
+ while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<Relatives") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
+ $lineoffset--;
+ }
+ $lineoffset++;
+ $reportxml = "<tempdoc>\n";
+ $line_nr = $lineoffset + $this->repeat_bytes;
+ // Relatives Level counter
+ $count = 1;
+ while (0 < $count) {
+ if (strpos($lines[$line_nr], "<Relatives") !== false) {
+ $count++;
+ } elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
+ $count--;
+ }
+ if (0 < $count) {
+ $reportxml .= $lines[$line_nr];
+ }
+ $line_nr++;
+ }
+ // No need to drag this
+ unset($lines);
+ $reportxml .= "</tempdoc>\n";
+ // Save original values
+ array_push($parserStack, $parser);
+ $oldgedrec = $this->gedrec;
+
+ $this->list_total = count($this->list);
+ $this->list_private = 0;
+ foreach ($this->list as $key => $value) {
+ if (isset($value->generation)) {
+ $this->generation = $value->generation;
+ }
+ $tmp = GedcomRecord::getInstance($key, $WT_TREE);
+ $this->gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
+
+ $repeat_parser = xml_parser_create();
+ $parser = $repeat_parser;
+ xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
+ xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
+ xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
+
+ if (!xml_parse($repeat_parser, $reportxml, true)) {
+ throw new \DomainException(sprintf("RelativesEHandler XML error: %s at line %d", xml_error_string(xml_get_error_code($repeat_parser)), xml_get_current_line_number($repeat_parser)));
+ }
+ xml_parser_free($repeat_parser);
+ }
+ // Clean up the list array
+ $this->list = array();
+ $parser = array_pop($parserStack);
+ $this->gedrec = $oldgedrec;
+ }
+ list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
+ }
+
+ /**
+ * XML <Generation /> element handler
+ *
+ * Prints the number of generations
+ */
+ function generationStartHandler() {
+ $this->current_element->addText($this->generation);
+ }
+
+ /**
+ * XML <NewPage /> element handler
+ *
+ * Has to be placed in an element (header, pageheader, body or footer)
+ */
+ function newPageStartHandler() {
+ global $wt_report;
+
+ $temp = "addpage";
+ $wt_report->addElement($temp);
+ }
+
+ /**
+ * @param array $attrs an array of key value pairs for the attributes
+ * @param string $tag HTML tag name
+ */
+ function htmlStartHandler($tag, $attrs) {
+ global $wt_reportStack, $wt_report, $ReportRoot;
+
+ if ($tag === "tempdoc") {
+ return;
+ }
+ array_push($wt_reportStack, $wt_report);
+ $wt_report = $ReportRoot->createHTML($tag, $attrs);
+ $this->current_element = $wt_report;
+
+ array_push($this->print_data_stack, $this->print_data);
+ $this->print_data = true;
+ }
+
+ /**
+ * @param string $tag
+ */
+ function htmlEndHandler($tag) {
+ global $wt_report, $wt_reportStack;
+ if ($tag === "tempdoc") {
+ return;
+ }
+
+ $this->print_data = array_pop($this->print_data_stack);
+ $this->current_element = $wt_report;
+ $wt_report = array_pop($wt_reportStack);
+ if (!is_null($wt_report)) {
+ $wt_report->addElement($this->current_element);
+ } else {
+ $wt_report = $this->current_element;
+ }
+ }
+
+ /**
+ * Handle <Input>
+ */
+ function inputStartHandler() {
+ // Dummy function, to prevent the default HtmlStartHandler() being called
+ }
+
+ /**
+ * Handle </Input>
+ */
+ function inputEndHandler() {
+ // Dummy function, to prevent the default HtmlEndHandler() being called
+ }
+
+ /**
+ * Handle <Report>
+ */
+ function reportStartHandler() {
+ // Dummy function, to prevent the default HtmlStartHandler() being called
+ }
+
+ /**
+ * Handle </Report>
+ */
+ function reportEndHandler() {
+ // Dummy function, to prevent the default HtmlEndHandler() being called
+ }
+
+ /**
+ * XML <titleStartHandler> start element handler
+ */
+ function titleStartHandler() {
+ $this->report_title = true;
+ }
+
+ /**
+ * XML </titleEndHandler> end element handler
+ */
+ function titleEndHandler() {
+ $this->report_title = false;
+ }
+
+ /**
+ * XML <descriptionStartHandler> start element handler
+ */
+ function descriptionStartHandler() {
+ $this->report_description = true;
+ }
+
+ /**
+ * XML </descriptionEndHandler> end element handler
+ */
+ function descriptionEndHandler() {
+ $this->report_description = false;
+ }
+}
diff --git a/app/Report/ReportParserSetup.php b/app/Report/ReportParserSetup.php
index 9add49857a..e9180d3433 100644
--- a/app/Report/ReportParserSetup.php
+++ b/app/Report/ReportParserSetup.php
@@ -20,7 +20,7 @@ use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\I18N;
/**
- * Class ReportParserSetup
+ * Class ReportParserSetup - parse a report.xml file and extract the setup options.
*/
class ReportParserSetup extends ReportParserBase {
/** @var array An array of report options/parameters */
diff --git a/reportengine.php b/reportengine.php
index 32882bb363..d58f3665a8 100644
--- a/reportengine.php
+++ b/reportengine.php
@@ -18,6 +18,7 @@ namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Report\ReportBaseElement;
use Fisharebest\Webtrees\Report\ReportHtml;
+use Fisharebest\Webtrees\Report\ReportParserGenerate;
use Fisharebest\Webtrees\Report\ReportPdf;
use Fisharebest\Webtrees\Report\ReportParserSetup;
@@ -288,264 +289,20 @@ case 'run':
}
/**
- * element handlers array
- *
- * Converts XML element names into functions
- *
- * @global string[][] $elementHandler
- */
- $elementHandler = array(
- 'AgeAtDeath' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\ageAtDeathStartHandler',
- ),
- 'br' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\brStartHandler',
- ),
- 'Body' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\bodyStartHandler',
- ),
- 'Cell' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\cellEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\cellStartHandler',
- ),
- 'Description' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\descriptionEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\descriptionStartHandler',
- ),
- 'Doc' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\docEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\docStartHandler',
- ),
- 'Report' => array(
- 'end' => '',
- 'start' => '',
- ),
- 'Facts' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\factsEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\factsStartHandler',
- ),
- 'Footer' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\footerStartHandler',
- ),
- 'Footnote' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\footnoteEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\footnoteStartHandler',
- ),
- 'FootnoteTexts' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\footnoteTextsStartHandler',
- ),
- 'Gedcom' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\gedcomEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\gedcomStartHandler',
- ),
- 'GedcomValue' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\gedcomValueStartHandler',
- ),
- 'Generation' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\generationStartHandler',
- ),
- 'GetPersonName' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\getPersonNameStartHandler',
- ),
- 'Header' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\headerStartHandler',
- ),
- 'HighlightedImage' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\highlightedImageStartHandler',
- ),
- 'if' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\ifEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\ifStartHandler',
- ),
- 'Image' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\imageStartHandler',
- ),
- 'Input' => array(
- 'end' => '',
- 'start'=> '',
- ),
- 'Line' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\lineStartHandler',
- ),
- 'List' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\listEndHandler',
- 'start'=> '\Fisharebest\Webtrees\Report\\listStartHandler',
- ),
- 'ListTotal' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\listTotalStartHandler',
- ),
- 'NewPage' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\newPageStartHandler',
- ),
- 'Now' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\nowStartHandler',
- ),
- 'PageHeader' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\pageHeaderEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\pageHeaderStartHandler',
- ),
- 'PageNum' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\pageNumStartHandler',
- ),
- 'Relatives' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\relativesEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\relativesStartHandler',
- ),
- 'RepeatTag' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\repeatTagEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\repeatTagStartHandler',
- ),
- 'SetVar' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\setVarStartHandler',
- ),
- 'Style' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\styleStartHandler',
- ),
- 'Text' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\textEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\textStartHandler',
- ),
- 'TextBox' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\textBoxEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\textBoxStartHandler',
- ),
- 'Title' => array(
- 'end' => '\Fisharebest\Webtrees\Report\\titleEndHandler',
- 'start' => '\Fisharebest\Webtrees\Report\\titleStartHandler',
- ),
- 'TotalPages' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\totalPagesStartHandler',
- ),
- 'var' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\varStartHandler',
- ),
- 'sp' => array(
- 'start' => '\Fisharebest\Webtrees\Report\\spStartHandler',
- ),
- );
-
- /**
- * A new object of the currently used element class
- *
- * @global ReportBaseElement $currentElement
- */
- $currentElement = new ReportBaseElement;
-
- /**
- * Should character data be printed
- *
- * This variable is turned on or off by the element handlers to tell whether the inner character
- * Data should be printed
- *
- * @global boolean $printData
- */
- $printData = false;
-
- /**
- * Title collector. Mark it if it has already been used
- *
- * @global boolean $reportTitle
- */
- $reportTitle = false;
-
- /**
- * Description collector. Mark it if it has already been used
- *
- * @global boolean $reportDescription
- */
- $reportDescription = false;
-
- /**
- * Print data stack
- *
- * As the XML is being processed there will be times when we need to turn on and off the
- * <var>$printData</var> variable as we encounter entinties in the XML. The stack allows us to
- * keep track of when to turn <var>$printData</var> on and off.
- *
- * @global array $printDataStack
- */
- $printDataStack = array();
-
- /**
* @global array $wt_reportStack
*/
$wt_reportStack = array();
/**
- * @global array $gedrecStack
- */
- $gedrecStack = array();
-
- /**
- * @global array $repeatsStack
- */
- $repeatsStack = array();
-
- /**
* @global array $parserStack
*/
$parserStack = array();
/**
- * @global array $repeats
- */
- $repeats = array();
-
- /**
- * @global string $gedrec
- */
- $gedrec = '';
-
- /**
- * @global ???? $repeatBytes
- */
- $repeatBytes = 0;
-
- /**
* @global resource $parser
*/
$parser = '';
- /**
- * @global int $processRepeats
- */
- $processRepeats = 0;
-
- /**
- * @global ???? $processIfs
- */
- $processIfs = 0;
-
- /**
- * @global ???? $processGedcoms
- */
- $processGedcoms = 0;
-
- /**
- * Wether or not to print footnote
- * true = print, false = don't print
- */
- $processFootnote = true;
-
- //-- start the sax parser
- $xml_parser = xml_parser_create();
- //-- make sure everything is case sensitive
- xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
- //-- set the main element handler functions
- xml_set_element_handler($xml_parser, '\Fisharebest\Webtrees\Report\\startElement', '\Fisharebest\Webtrees\Report\\endElement');
- //-- set the character data handler
- xml_set_character_data_handler($xml_parser, '\Fisharebest\Webtrees\Report\\characterData');
-
- $fp = fopen($report, 'r');
- while (($data = fread($fp, 4096))) {
- if (!xml_parse($xml_parser, $data, feof($fp))) {
- throw new \DomainException(sprintf(
- 'XML error: %s at line %d',
- xml_error_string(xml_get_error_code($xml_parser)),
- xml_get_current_line_number($xml_parser)
- ));
- }
- }
- xml_parser_free($xml_parser);
+ new ReportParserGenerate($report);
}