diff options
Diffstat (limited to 'library')
69 files changed, 2634 insertions, 2900 deletions
diff --git a/library/WT/Controller/AdvancedSearch.php b/library/WT/Controller/AdvancedSearch.php index cba844cd74..586db82939 100644 --- a/library/WT/Controller/AdvancedSearch.php +++ b/library/WT/Controller/AdvancedSearch.php @@ -2,7 +2,7 @@ // Controller for the advanced search page // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -202,7 +202,7 @@ class WT_Controller_AdvancedSearch extends WT_Controller_Search { } // Dynamic SQL query, plus bind variables - $sql="SELECT DISTINCT 'INDI' AS type, ind.i_id AS xref, ind.i_file AS ged_id, ind.i_gedcom AS gedrec FROM `##individuals` ind"; + $sql="SELECT DISTINCT ind.i_id AS xref, ind.i_file AS gedcom_id, ind.i_gedcom AS gedcom FROM `##individuals` ind"; $bind=array(); // Join the following tables @@ -486,13 +486,13 @@ class WT_Controller_AdvancedSearch extends WT_Controller_Search { $bind[]=$value; } } - $rows=WT_DB::prepare($sql)->execute($bind)->fetchAll(PDO::FETCH_ASSOC); + $rows=WT_DB::prepare($sql)->execute($bind)->fetchAll(); foreach ($rows as $row) { - $person=WT_Person::getInstance($row); + $person=WT_Individual::getInstance($row->xref, $row->gedcom_id, $row->gedcom); // Check for XXXX:PLAC fields, which were only partially matched by SQL foreach ($this->fields as $n=>$field) { if ($this->values[$n] && preg_match('/^('.WT_REGEX_TAG.'):PLAC$/', $field, $match)) { - if (!preg_match('/\n1 '.$match[1].'(\n[2-9].*)*\n2 PLAC .*'.preg_quote($this->values[$n], '/').'/i', $person->getGedcomRecord())) { + if (!preg_match('/\n1 '.$match[1].'(\n[2-9].*)*\n2 PLAC .*'.preg_quote($this->values[$n], '/').'/i', $person->getGedcom())) { continue 2; } } diff --git a/library/WT/Controller/Ancestry.php b/library/WT/Controller/Ancestry.php index 6228418cdc..23bafdd5b5 100644 --- a/library/WT/Controller/Ancestry.php +++ b/library/WT/Controller/Ancestry.php @@ -2,7 +2,7 @@ // Controller for the ancestry chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -74,7 +74,7 @@ class WT_Controller_Ancestry extends WT_Controller_Chart { $pbwidth = $bwidth+12; $pbheight = $bheight+14; - if ($this->root && $this->root->canDisplayName()) { + if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: %s is an individual’s name */ WT_I18N::translate('Ancestors of %s', $this->root->getFullName()) diff --git a/library/WT/Controller/Chart.php b/library/WT/Controller/Chart.php index 6e54c135d7..ce9f1d9726 100644 --- a/library/WT/Controller/Chart.php +++ b/library/WT/Controller/Chart.php @@ -35,14 +35,14 @@ class WT_Controller_Chart extends WT_Controller_Page { $this->rootid = safe_GET_xref('rootid'); if ($this->rootid) { - $this->root = WT_Person::getInstance($this->rootid); + $this->root = WT_Individual::getInstance($this->rootid); } else { // Missing rootid parameter? Do something. $this->root = $this->getSignificantIndividual(); $this->rootid = $this->root->getXref(); } - if (!$this->root || !$this->root->canDisplayName()) { + if (!$this->root || !$this->root->canShowName()) { header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden'); $this->error_message=WT_I18N::translate('This individual does not exist or you do not have permission to view it.'); $this->rootid=null; diff --git a/library/WT/Controller/Compact.php b/library/WT/Controller/Compact.php index d8b051e248..5bda1695b1 100644 --- a/library/WT/Controller/Compact.php +++ b/library/WT/Controller/Compact.php @@ -2,7 +2,7 @@ // Controller for the compact chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -41,7 +41,7 @@ class WT_Controller_Compact extends WT_Controller_Chart { // Extract the request parameters $this->show_thumbs=safe_GET_bool('show_thumbs'); - if ($this->root && $this->root->canDisplayName()) { + if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: %s is an individual’s name */ WT_I18N::translate('Compact tree of %s', $this->root->getFullName()) @@ -55,9 +55,9 @@ class WT_Controller_Compact extends WT_Controller_Chart { function sosa_person($n) { global $SHOW_HIGHLIGHT_IMAGES; - $indi=WT_Person::getInstance($this->treeid[$n]); + $indi=WT_Individual::getInstance($this->treeid[$n]); - if ($indi && $indi->canDisplayName()) { + if ($indi && $indi->canShowName()) { $name=$indi->getFullName(); $addname=$indi->getAddName(); @@ -72,7 +72,7 @@ class WT_Controller_Compact extends WT_Controller_Chart { if ($addname) $html .= '<br>' . $addname; $html .= '</a>'; $html .= '<br>'; - if ($indi->canDisplayDetails()) { + if ($indi->canShow()) { $html.='<div class="details1">'.$indi->getLifeSpan().'</div>'; } } else { @@ -115,7 +115,7 @@ class WT_Controller_Compact extends WT_Controller_Chart { } if ($pid) { - $indi=WT_Person::getInstance($pid); + $indi=WT_Individual::getInstance($pid); $title=WT_I18N::translate('Compact tree of %s', $indi->getFullName()); $text = '<a class="icon-'.$arrow_dir.'arrow" title="'.strip_tags($title).'" href="?rootid='.$pid; if ($this->show_thumbs) $text .= "&show_thumbs=".$this->show_thumbs; diff --git a/library/WT/Controller/Descendancy.php b/library/WT/Controller/Descendancy.php index f9d07c6977..9ee2c3d733 100644 --- a/library/WT/Controller/Descendancy.php +++ b/library/WT/Controller/Descendancy.php @@ -2,7 +2,7 @@ // Controller for the descendancy chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -92,7 +92,7 @@ class WT_Controller_Descendancy extends WT_Controller_Chart { $this->cellwidth=(strlen($this->name)*14); } - if ($this->root && $this->root->canDisplayName()) { + if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: %s is an individual’s name */ WT_I18N::translate('Descendants of %s', $this->root->getFullName()) @@ -199,7 +199,7 @@ class WT_Controller_Descendancy extends WT_Controller_Chart { $spouse=$family->getSpouse($person); if (!$spouse) { // One parent families have no spouse - $spouse=new WT_Person(''); + $spouse=new WT_Individual(''); } // print marriage info @@ -244,7 +244,7 @@ class WT_Controller_Descendancy extends WT_Controller_Chart { } else { // Distinguish between no children (NCHI 0) and no recorded // children (no CHIL records) - if (strpos($family->getGedcomRecord(), '\n1 NCHI 0')) { + if (strpos($family->getGedcom(), '\n1 NCHI 0')) { echo WT_Gedcom_Tag::getLabel('NCHI').': '.count($children); } else { echo WT_I18N::translate('No children'); diff --git a/library/WT/Controller/Family.php b/library/WT/Controller/Family.php index 68151a0d35..686627562b 100644 --- a/library/WT/Controller/Family.php +++ b/library/WT/Controller/Family.php @@ -2,7 +2,7 @@ // Controller for the family page // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2010 PGV Development Team. All rights reserved. @@ -32,46 +32,14 @@ require_once WT_ROOT.'includes/functions/functions_print_facts.php'; require_once WT_ROOT.'includes/functions/functions_import.php'; class WT_Controller_Family extends WT_Controller_GedcomRecord { - var $diff_record; - var $record = null; - var $user = null; - var $display = false; - var $famrec = ''; - var $title = ''; - public function __construct() { global $Dbwidth, $bwidth, $pbwidth, $pbheight, $bheight; - $bwidth = $Dbwidth; - $pbwidth = $bwidth + 12; + $bwidth = $Dbwidth; + $pbwidth = $bwidth + 12; $pbheight = $bheight + 14; - $xref = safe_GET_xref('famid'); - - $gedrec = find_family_record($xref, WT_GED_ID); - - if (empty($gedrec)) { - $gedrec = "0 @".$xref."@ FAM\n"; - } - - if (find_family_record($xref, WT_GED_ID) || find_updated_record($xref, WT_GED_ID)!==null) { - $this->record = new WT_Family($gedrec); - $this->record->ged_id=WT_GED_ID; // This record is from a file - } else if (!$this->record) { - parent::__construct(); - return; - } - - $xref=$this->record->getXref(); // Correct upper/lower case mismatch - - //-- if the user can edit and there are changes then get the new changes - if (WT_USER_CAN_EDIT) { - $newrec = find_updated_record($xref, WT_GED_ID); - if (!empty($newrec)) { - $this->diff_record = new WT_Family($newrec); - $this->diff_record->setChanged(true); - $this->record->diffMerge($this->diff_record); - } - } + $xref = safe_GET_xref('famid'); + $this->record = WT_Family::getInstance($xref); parent::__construct(); } @@ -98,7 +66,7 @@ class WT_Controller_Family extends WT_Controller_GedcomRecord { // $tags is an array of HUSB/WIFE/CHIL function getTimelineIndis($tags) { - preg_match_all('/\n1 (?:'.implode('|', $tags).') @('.WT_REGEX_XREF.')@/', $this->record->getGedcomRecord(), $matches); + preg_match_all('/\n1 (?:'.implode('|', $tags).') @('.WT_REGEX_XREF.')@/', $this->record->getGedcom(), $matches); foreach ($matches[1] as &$match) { $match='pids[]='.$match; } @@ -111,7 +79,7 @@ class WT_Controller_Family extends WT_Controller_GedcomRecord { function getEditMenu() { $SHOW_GEDCOM_RECORD=get_gedcom_setting(WT_GED_ID, 'SHOW_GEDCOM_RECORD'); - if (!$this->record || $this->record->isMarkedDeleted()) { + if (!$this->record || $this->record->isOld()) { return null; } @@ -138,21 +106,6 @@ class WT_Controller_Family extends WT_Controller_GedcomRecord { } } - // edit/view raw gedcom - if (WT_USER_IS_ADMIN || $SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('Edit raw GEDCOM record'), '#', 'menu-fam-editraw'); - $submenu->addOnclick("return edit_raw('".$this->record->getXref()."');"); - $menu->addSubmenu($submenu); - } elseif ($SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('View GEDCOM Record'), '#', 'menu-fam-viewraw'); - if (WT_USER_CAN_EDIT) { - $submenu->addOnclick("return show_gedcom_record('new');"); - } else { - $submenu->addOnclick("return show_gedcom_record();"); - } - $menu->addSubmenu($submenu); - } - // delete if (WT_USER_CAN_EDIT) { $submenu = new WT_Menu(WT_I18N::translate('Delete'), '#', 'menu-fam-del'); @@ -202,7 +155,6 @@ class WT_Controller_Family extends WT_Controller_GedcomRecord { foreach ($indifacts as $fact) { print_fact($fact, $this->record); } - print_main_media($this->record->getXref()); } else { echo '<tr><td class="messagebox" colspan="2">', WT_I18N::translate('No facts for this family.'), '</td></tr>'; } diff --git a/library/WT/Controller/Familybook.php b/library/WT/Controller/Familybook.php index e75a46b6a6..c509a10af4 100644 --- a/library/WT/Controller/Familybook.php +++ b/library/WT/Controller/Familybook.php @@ -2,7 +2,7 @@ // Controller for the familybook chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -64,7 +64,7 @@ class WT_Controller_Familybook extends WT_Controller_Chart { // Validate parameters if (!empty($rootid)) $this->pid = $rootid; $rootid=$this->rootid; - $this->hourPerson = WT_Person::getInstance($this->pid); + $this->hourPerson = WT_Individual::getInstance($this->pid); if (!$this->hourPerson) { $this->hourPerson=$this->getSignificantIndividual(); $this->pid=$this->hourPerson->getXref(); @@ -75,7 +75,7 @@ class WT_Controller_Familybook extends WT_Controller_Chart { $bheight = $cbheight; } $bhalfheight = $bheight / 2; - if ($this->root && $this->root->canDisplayName()) { + if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: %s is an individual’s name */ WT_I18N::translate('Family book of %s', $this->root->getFullName()) @@ -334,7 +334,7 @@ class WT_Controller_Familybook extends WT_Controller_Chart { */ function max_descendency_generations($pid, $depth) { if ($depth > $this->generations) return $depth; - $person = WT_Person::getInstance($pid); + $person = WT_Individual::getInstance($pid); if (is_null($person)) return $depth; $maxdc = $depth; foreach ($person->getSpouseFamilies() as $family) { @@ -360,7 +360,7 @@ class WT_Controller_Familybook extends WT_Controller_Chart { } function print_family_book($person, $descent) { global $firstrun; - if ($descent==0 || !$person->canDisplayName()) { + if ($descent==0 || !$person->canShowName()) { return; } $families=$person->getSpouseFamilies(); diff --git a/library/WT/Controller/Fanchart.php b/library/WT/Controller/Fanchart.php index 2b86dab1d6..ea6edf7c12 100644 --- a/library/WT/Controller/Fanchart.php +++ b/library/WT/Controller/Fanchart.php @@ -2,7 +2,7 @@ // Controller for the fan chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -46,7 +46,7 @@ class WT_Controller_Fanchart extends WT_Controller_Chart { $this->fan_width =safe_GET_integer('fan_width', 50, 300, 100); $this->generations=safe_GET_integer('generations', 2, 9, $default_generations); - if ($this->root && $this->root->canDisplayName()) { + if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: http://en.wikipedia.org/wiki/Family_tree#Fan_chart - %s is an individual’s name */ WT_I18N::translate('Fan chart of %s', $this->root->getFullName()) @@ -211,7 +211,7 @@ class WT_Controller_Fanchart extends WT_Controller_Chart { while ($sosa >= $p2) { $pid=$treeid[$sosa]; if ($pid) { - $person =WT_Person::getInstance($pid); + $person =WT_Individual::getInstance($pid); $name =$person->getFullName(); $addname=$person->getAddName(); @@ -234,8 +234,8 @@ class WT_Controller_Fanchart extends WT_Controller_Chart { $text .= $person->getLifeSpan(); - $text = unhtmlentities($text); $text = strip_tags($text); + $text = htmlspecialchars_decode($text); // split and center text by lines $wmax = (int)($angle*7/$fanChart['size']*$scale); diff --git a/library/WT/Controller/GedcomRecord.php b/library/WT/Controller/GedcomRecord.php index 18c64bd9fa..233c7fbaac 100644 --- a/library/WT/Controller/GedcomRecord.php +++ b/library/WT/Controller/GedcomRecord.php @@ -29,6 +29,22 @@ class WT_Controller_GedcomRecord extends WT_Controller_Page { public $record; // individual, source, repository, etc. public function __construct() { + // Automatically fix broken links + if ($this->record && $this->record->canEdit()) { + $broken_links=0; + foreach ($this->record->getFacts('HUSB|WIFE|CHIL|FAMS|FAMC|SOUR|REPO|OBJE') as $fact) { // Not NOTE! + if (!$fact->isOld() && $fact->getTarget() === null) { + $this->record->updateFact($fact->getFactId(), null, false); + WT_FlashMessages::addMessage(/* I18N: %s are names of records, such as sources, repositories or individuals */ WT_I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $this->record->getFullName(), $fact->getValue())); + $broken_links = true; + } + } + if ($broken_links) { + // Reload the updated family + $this->record = WT_GedcomRecord::getInstance($this->record->getXref()); + } + } + parent::__construct(); // We want robots to index this page @@ -37,7 +53,7 @@ class WT_Controller_GedcomRecord extends WT_Controller_Page { // Set a page title if ($this->record) { $this->setCanonicalUrl($this->record->getHtmlUrl()); - if ($this->record->canDisplayName()) { + if ($this->record->canShowName()) { // e.g. "John Doe" or "1881 Census of Wales" $this->setPageTitle($this->record->getFullName()); } else { diff --git a/library/WT/Controller/Hourglass.php b/library/WT/Controller/Hourglass.php index 7dee4869e3..01285b296b 100644 --- a/library/WT/Controller/Hourglass.php +++ b/library/WT/Controller/Hourglass.php @@ -2,7 +2,7 @@ // Controller for the hourglass chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -90,7 +90,7 @@ class WT_Controller_Hourglass extends WT_Controller_Chart { $bhalfheight = (int)($bheight / 2); // Validate parameters - $this->hourPerson = WT_Person::getInstance($this->pid); + $this->hourPerson = WT_Individual::getInstance($this->pid); if (!$this->hourPerson) { $this->hourPerson=$this->getSignificantIndividual(); $this->pid=$this->hourPerson->getXref(); @@ -347,7 +347,7 @@ class WT_Controller_Hourglass extends WT_Controller_Chart { // For the root person, print a down arrow that allows changing the root of tree if ($showNav && $count==1) { // NOTE: If statement OK - if ($person->canDisplayName()) { + if ($person->canShowName()) { // -- print left arrow for decendants so that we can move down the tree $famids = $person->getSpouseFamilies(); //-- make sure there is more than 1 child in the family with parents @@ -435,7 +435,7 @@ class WT_Controller_Hourglass extends WT_Controller_Chart { */ function max_descendency_generations($pid, $depth) { if ($depth > $this->generations) return $depth; - $person = WT_Person::getInstance($pid); + $person = WT_Individual::getInstance($pid); if (is_null($person)) return $depth; $maxdc = $depth; foreach ($person->getSpouseFamilies() as $family) { diff --git a/library/WT/Controller/Individual.php b/library/WT/Controller/Individual.php index 26cf33b4ec..c09304fa32 100644 --- a/library/WT/Controller/Individual.php +++ b/library/WT/Controller/Individual.php @@ -32,58 +32,29 @@ require_once WT_ROOT.'includes/functions/functions_print_facts.php'; require_once WT_ROOT.'includes/functions/functions_import.php'; class WT_Controller_Individual extends WT_Controller_GedcomRecord { - var $name_count = 0; - var $total_names = 0; - var $SEX_COUNT = 0; - var $Fam_Navigator = 'YES'; - var $NAME_LINENUM = null; - var $SEX_LINENUM = null; - var $globalfacts = null; + public $name_count = 0; + public $total_names = 0; + public $tabs; function __construct() { - global $USE_RIN, $MAX_ALIVE_AGE, $SEARCH_SPIDER; - global $DEFAULT_PIN_STATE, $DEFAULT_SB_CLOSED_STATE; - global $Fam_Navigator; - - $xref = safe_GET_xref('pid'); - - $gedrec = find_person_record($xref, WT_GED_ID); - - if ($USE_RIN && $gedrec==false) { - $xref = find_rin_id($xref); - $gedrec = find_person_record($xref, WT_GED_ID); - } - if (empty($gedrec)) { - $gedrec = "0 @".$xref."@ INDI\n"; - } + global $USE_RIN; - if (find_person_record($xref, WT_GED_ID) || find_updated_record($xref, WT_GED_ID)!==null) { - $this->record = new WT_Person($gedrec); - $this->record->ged_id=WT_GED_ID; // This record is from a file - } else if (!$this->record) { - parent::__construct(); - return; - } + $xref = safe_GET_xref('pid'); + $this->record = WT_Individual::getInstance($xref); - //-- if the user can edit and there are changes then get the new changes - if (WT_USER_CAN_EDIT || WT_USER_CAN_ACCEPT) { - $newrec = find_updated_record($xref, WT_GED_ID); - if (!empty($newrec)) { - $diff_record = new WT_Person($newrec); - $diff_record->setChanged(true); - $this->record->diffMerge($diff_record); - } + if (!$this->record && $USE_RIN) { + $rin = find_rin_id($xref); + $this->record = WT_Individual::getInstance($rin); } - $this->tabs=WT_Module::getActiveTabs(); - - // Our parent needs $this->record parent::__construct(); + $this->tabs = WT_Module::getActiveTabs(); + // If we can display the details, add them to the page header - if ($this->record && $this->record->canDisplayDetails()) { - $this->setPageTitle($this->record->getFullName().' '.$this->record->getLifespan()); + if ($this->record && $this->record->canShow()) { + $this->setPageTitle($this->record->getFullName() . ' ' . $this->record->getLifespan()); } } @@ -147,29 +118,33 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { * @see individual.php * @param Event $event the event object */ - function print_name_record(WT_Event $event) { + function print_name_record(WT_Fact $event) { if (!$event->canShow()) { return false; } - $factrec = $event->getGedComRecord(); - $linenum = $event->getLineNumber(); + $factrec = $event->getGedcom(); // Create a dummy record, so we can extract the formatted NAME value from the event. - $dummy=new WT_Person('0 @'.$event->getParentObject()->getXref()."@ INDI\n1 DEAT Y\n".$factrec); + $dummy=new WT_Individual( + 'xref', + "0 @xref@ INDI\n1 DEAT Y\n".$factrec, + null, + WT_GED_ID + ); $all_names=$dummy->getAllNames(); $primary_name=$all_names[0]; $this->name_count++; if ($this->name_count >1) { echo '<h3 class="name_two">',$dummy->getFullName(), '</h3>'; } //Other names accordion element - echo '<div class="indi_name_details"'; - if ($event->getIsOld()) { - echo " class=\"namered\""; + echo '<div class="indi_name_details'; + if ($event->isOld()) { + echo ' old'; } - if ($event->getIsNew()) { - echo " class=\"nameblue\""; + if ($event->isNew()) { + echo ' new'; } - echo ">"; + echo '">'; echo '<div class="name1">'; echo '<dl><dt class="label">', WT_I18N::translate('Name'), '</dt>'; @@ -184,9 +159,9 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { } } } - if ($this->record->canEdit() && !$event->getIsOld()) { - echo "<div class=\"deletelink\"><a class=\"font9 deleteicon\" href=\"#\" onclick=\"return delete_fact('".$this->record->getXref()."', ".$linenum.", '', '".WT_I18N::translate('Are you sure you want to delete this fact?')."');\" title=\"".WT_I18N::translate('Delete name')."\"><span class=\"link_text\">".WT_I18N::translate('Delete name')."</span></a></div>"; - echo "<div class=\"editlink\"><a href=\"#\" class=\"font9 editicon\" onclick=\"edit_name('".$this->record->getXref()."', ".$linenum."); return false;\" title=\"".WT_I18N::translate('Edit name')."\"><span class=\"link_text\">".WT_I18N::translate('Edit name')."</span></a></div>"; + if ($this->record->canEdit() && !$event->isOld()) { + echo "<div class=\"deletelink\"><a class=\"font9 deleteicon\" href=\"#\" onclick=\"return delete_fact('".WT_I18N::translate('Are you sure you want to delete this fact?')."', '".$this->record->getXref()."', '".$event->getFactId()."');\" title=\"".WT_I18N::translate('Delete name')."\"><span class=\"link_text\">".WT_I18N::translate('Delete name')."</span></a></div>"; + echo "<div class=\"editlink\"><a href=\"#\" class=\"font9 editicon\" onclick=\"edit_name('".$this->record->getXref()."', '".$event->getFactId()."'); return false;\" title=\"".WT_I18N::translate('Edit name')."\"><span class=\"link_text\">".WT_I18N::translate('Edit name')."</span></a></div>"; } echo '</dd>'; echo '</dl>'; @@ -243,50 +218,45 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { * @see individual.php * @param Event $event the Event object */ - function print_sex_record(WT_Event $event) { - global $sex; - - if (!$event->canShow()) return false; - $factrec = $event->getGedComRecord(); - $sex = $event->getDetail(); + function print_sex_record(WT_Fact $event) { + $sex = $event->getValue(); if (empty($sex)) $sex = 'U'; - echo '<span id="sex"'; - echo ' class="'; - if ($event->getIsOld()) { - echo 'namered '; - } - if ($event->getIsNew()) { - echo 'nameblue '; - } - switch ($sex) { - case 'M': - echo 'male_gender"'; - if ($this->record->canEdit() && !$event->getIsOld()) { - echo ' title="', WT_I18N::translate('Male'), ' - ', WT_I18N::translate('Edit'), '"'; - echo ' onclick="edit_record(\''.$this->record->getXref().'\', '.$event->getLineNumber().'); return false;"> '; - } else { - echo ' title="', WT_I18N::translate('Male'), '"> '; - } - break; - case 'F': - echo 'female_gender"'; - if ($this->record->canEdit() && !$event->getIsOld()) { - echo ' title="', WT_I18N::translate('Female'), ' - ', WT_I18N::translate('Edit'), '"'; - echo ' onclick="edit_record(\''.$this->record->getXref().'\', '.$event->getLineNumber().'); return false;"> '; - } else { - echo ' title="', WT_I18N::translate('Female'), '"> '; - } - break; - case 'U': - echo 'unknown_gender"'; - if ($this->record->canEdit() && !$event->getIsOld()) { - echo ' title="', WT_I18N::translate_c('unknown gender', 'Unknown'), ' - ', WT_I18N::translate('Edit'), '"'; - echo ' onclick="edit_record(\''.$this->record->getXref().'\', '.$event->getLineNumber().'); return false;"> '; - } else { - echo ' title="', WT_I18N::translate_c('unknown gender', 'Unknown'), '"> '; - } - break; - } + echo '<span id="sex" class="'; + if ($event->isOld()) { + echo 'old'; + } + if ($event->isNew()) { + echo 'new'; + } + switch ($sex) { + case 'M': + echo ' male_gender"'; + if ($event->canEdit()) { + echo ' title="', WT_I18N::translate('Male'), ' - ', WT_I18N::translate('Edit'), '"'; + echo ' onclick="edit_record(\''.$this->record->getXref().'\', \''.$event->getFactId().'\'); return false;">'; + } else { + echo ' title="', WT_I18N::translate('Male'), '">'; + } + break; + case 'F': + echo ' female_gender"'; + if ($event->canEdit()) { + echo ' title="', WT_I18N::translate('Female'), ' - ', WT_I18N::translate('Edit'), '"'; + echo ' onclick="edit_record(\''.$this->record->getXref().'\', \''.$event->getFactId().'\'); return false;">'; + } else { + echo ' title="', WT_I18N::translate('Female'), '">'; + } + break; + case 'U': + echo ' unknown_gender"'; + if ($event->canEdit()) { + echo ' title="', WT_I18N::translate_c('unknown gender', 'Unknown'), ' - ', WT_I18N::translate('Edit'), '"'; + echo ' onclick="edit_record(\''.$this->record->getXref().'\', \''.$event->getFactId().'\'); return false;">'; + } else { + echo ' title="', WT_I18N::translate_c('unknown gender', 'Unknown'), '">'; + } + break; + } echo '</span>'; } /** @@ -295,39 +265,41 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { function getEditMenu() { $SHOW_GEDCOM_RECORD=get_gedcom_setting(WT_GED_ID, 'SHOW_GEDCOM_RECORD'); - if (!$this->record || $this->record->isMarkedDeleted()) { + if (!$this->record || $this->record->isOld()) { return null; } // edit menu $menu = new WT_Menu(WT_I18N::translate('Edit'), '#', 'menu-indi'); $menu->addLabel($menu->label, 'down'); - $this->getGlobalFacts(); // sets NAME_LINENUM and SEX_LINENUM. individual.php doesn't do it early enough for us.... - // What behaviour shall we give the main menu? If we leave it blank, the framework // will copy the first submenu - which may be edit-raw or delete. // As a temporary solution, make it edit the name - if (WT_USER_CAN_EDIT && $this->NAME_LINENUM) { - $menu->addOnclick("return edit_name('".$this->record->getXref()."', ".$this->NAME_LINENUM.");"); - } else { - $menu->addOnclick("return false;"); - } - + $menu->addOnclick("return false;"); if (WT_USER_CAN_EDIT) { - //--make sure the totals are correct + foreach ($this->record->getFacts() as $fact) { + if ($fact->getTag()=='NAME' && $fact->canShow() && $fact->canEdit()) + $menu->addOnclick("return edit_name('".$this->record->getXref() . "', '" . $fact->getFactId() . "');"); + break; + } + $submenu = new WT_Menu(WT_I18N::translate('Add new Name'), '#', 'menu-indi-addname'); $submenu->addOnclick("return add_name('".$this->record->getXref()."');"); $menu->addSubmenu($submenu); - if ($this->SEX_COUNT<2) { - $submenu = new WT_Menu(WT_I18N::translate('Edit gender'), '#', 'menu-indi-editsex'); - if ($this->SEX_LINENUM=="new") { - $submenu->addOnclick("return add_new_record('".$this->record->getXref()."', 'SEX');"); - } else { - $submenu->addOnclick("return edit_record('".$this->record->getXref()."', ".$this->SEX_LINENUM.");"); + $has_sex_record = false; + $submenu = new WT_Menu(WT_I18N::translate('Edit gender'), '#', 'menu-indi-editsex'); + foreach ($this->record->getFacts() as $fact) { + if ($fact->getTag()=='SEX' && $fact->canShow() && $fact->canEdit()) { + $submenu->addOnclick("return edit_record('" . $this->record->getXref() . "', '" . $fact->getFactId() . "');"); + $has_sex_record = true; + break; } - $menu->addSubmenu($submenu); } + if (!$has_sex_record) { + $submenu->addOnclick("return add_new_record('" . $this->record->getXref() . "', 'SEX');"); + } + $menu->addSubmenu($submenu); if (count($this->record->getSpouseFamilies())>1) { $submenu = new WT_Menu(WT_I18N::translate('Re-order families'), '#', 'menu-indi-orderfam'); @@ -336,21 +308,6 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { } } - // edit/view raw gedcom - if (WT_USER_IS_ADMIN || $SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('Edit raw GEDCOM record'), '#', 'menu-indi-editraw'); - $submenu->addOnclick("return edit_raw('".$this->record->getXref()."');"); - $menu->addSubmenu($submenu); - } elseif ($SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('View GEDCOM Record'), '#', 'menu-indi-viewraw'); - if (WT_USER_CAN_EDIT) { - $submenu->addOnclick("return show_gedcom_record('new');"); - } else { - $submenu->addOnclick("return show_gedcom_record();"); - } - $menu->addSubmenu($submenu); - } - // delete if (WT_USER_CAN_EDIT) { $submenu = new WT_Menu(WT_I18N::translate('Delete'), '#', 'menu-indi-del'); @@ -372,48 +329,7 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { return $menu; } - /** - * get global facts - * global facts are NAME and SEX - * @return array return the array of global facts - */ - function getGlobalFacts() { - if ($this->globalfacts==null) { - $this->globalfacts = $this->record->getGlobalFacts(); - foreach ($this->globalfacts as $key => $value) { - $fact = $value->getTag(); - if ($fact=="SEX") { - $this->SEX_COUNT++; - $this->SEX_LINENUM = $value->getLineNumber(); - } - if ($fact=="NAME") { - $this->total_names++; - if ($this->NAME_LINENUM==null && !$value->getIsOld()) { - // This is the "primary" name and is edited from the menu - // Subsequent names get their own edit links - $this->NAME_LINENUM = $value->getLineNumber(); - } - } - } - } - return $this->globalfacts; - } - /** - * get the individual facts shown on tab 1 - * @return array - */ - function getIndiFacts() { - $indifacts = $this->record->getIndiFacts(); - sort_facts($indifacts); - return $indifacts; - } - /** - * get the other facts shown on tab 2 - * @return array - */ - function getOtherFacts() { - $otherfacts = $this->record->getOtherFacts(); - return $otherfacts; + function add_asso_facts() { } /** @@ -514,22 +430,10 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { $newwife = null; $newchildren = array(); $delchildren = array(); - $children = array(); - $husb = null; - $wife = null; - if (!$family->getChanged()) { - $husb = $family->getHusband(); - $wife = $family->getWife(); - $children = $family->getChildren(); - } - //-- step families : set the label for the common parent - if ($type=="step-parents") { - $fams = $this->record->getChildFamilies(); - foreach ($fams as $key=>$fam) { - if ($fam->hasParent($husb)) $labels["father"] = get_relationship_name_from_path('fat', null, null); - if ($fam->hasParent($wife)) $labels["mother"] = get_relationship_name_from_path('mot', null, null); - } - } + $husb = $family->getHusband(); + $wife = $family->getWife(); + $children = $family->getChildren(); + //-- set the label for the husband if (!is_null($husb)) { $label = $labels["parent"]; @@ -560,80 +464,6 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { } $wife->setLabel($label); } - if (WT_USER_CAN_EDIT || WT_USER_CAN_ACCEPT) { - $newfamily = $family->getUpdatedFamily(); - if (!is_null($newfamily)) { - $newhusb = $newfamily->getHusband(); - //-- check if the husband in the family has changed - if (!is_null($newhusb) && !$newhusb->equals($husb)) { - $label = $labels["parent"]; - $sex = $newhusb->getSex(); - if ($sex=="F") { - $label = $labels["mother"]; - } - if ($sex=="M") { - $label = $labels["father"]; - } - if ($newhusb->getXref()==$this->record->getXref()) { - $label = '<i class="icon-selected"></i>'; - } - $newhusb->setLabel($label); - } - else $newhusb = null; - $newwife = $newfamily->getWife(); - //-- check if the wife in the family has changed - if (!is_null($newwife) && !$newwife->equals($wife)) { - $label = $labels["parent"]; - $sex = $newwife->getSex(); - if ($sex=="F") { - $label = $labels["mother"]; - } - if ($sex=="M") { - $label = $labels["father"]; - } - if ($newwife->getXref()==$this->record->getXref()) { - $label = '<i class="icon-selected"></i>'; - } - $newwife->setLabel($label); - } - else $newwife = null; - //-- check for any new children - $merged_children = array(); - $new_children = $newfamily->getChildren(); - $num = count($children); - for ($i=0; $i<$num; $i++) { - $child = $children[$i]; - if (!is_null($child)) { - $found = false; - foreach ($new_children as $key=>$newchild) { - if (!is_null($newchild)) { - if ($child->equals($newchild)) { - $found = true; - break; - } - } - } - if (!$found) $delchildren[] = $child; - else $merged_children[] = $child; - } - } - foreach ($new_children as $key=>$newchild) { - if (!is_null($newchild)) { - $found = false; - foreach ($children as $key1=>$child) { - if (!is_null($child)) { - if ($child->equals($newchild)) { - $found = true; - break; - } - } - } - if (!$found) $newchildren[] = $newchild; - } - } - $children = $merged_children; - } - } //-- set the labels for the children $num = count($children); for ($i=0; $i<$num; $i++) { @@ -650,7 +480,7 @@ class WT_Controller_Individual extends WT_Controller_GedcomRecord { $label = '<i class="icon-selected"></i>'; } if ($include_pedi==true) { - $famcrec = get_sub_record(1, "1 FAMC @".$family->getXref()."@", $children[$i]->getGedcomRecord()); + $famcrec = get_sub_record(1, "1 FAMC @".$family->getXref()."@", $children[$i]->getGedcom()); $pedi = get_gedcom_value("PEDI", 2, $famcrec); if ($pedi) { $label.='<br>('.WT_Gedcom_Code_Pedi::getValue($pedi, $children[$i]).')'; diff --git a/library/WT/Controller/Lifespan.php b/library/WT/Controller/Lifespan.php index 60c6664289..4c4f325d30 100644 --- a/library/WT/Controller/Lifespan.php +++ b/library/WT/Controller/Lifespan.php @@ -122,7 +122,7 @@ class WT_Controller_Lifespan extends WT_Controller_Page { } } } elseif ($newpid) { - $person=WT_Person::getInstance($newpid); + $person=WT_Individual::getInstance($newpid); $this->addFamily($person, $addfam); } elseif (!$this->pids) { $this->addFamily($this->getSignificantIndividual(), false); @@ -138,13 +138,13 @@ class WT_Controller_Lifespan extends WT_Controller_Page { foreach ($this->pids as $key => $value) { if ($value != $remove) { $this->pids[$key] = $value; - $person = WT_Person::getInstance($value); + $person = WT_Individual::getInstance($value); // list of linked records includes families as well as individuals. if ($person) { $bdate = $person->getEstimatedBirthDate(); $ddate = $person->getEstimatedDeathDate(); //--Checks to see if the details of that person can be viewed - if ($bdate->isOK() && $person->canDisplayDetails()) { + if ($bdate->isOK() && $person->canShow()) { $this->people[] = $person; } } @@ -169,7 +169,7 @@ class WT_Controller_Lifespan extends WT_Controller_Page { $bdate = $person->getEstimatedBirthDate(); $ddate = $person->getEstimatedDeathDate(); //--Checks to see if the details of that person can be viewed - if ($bdate->isOK() && $person->canDisplayDetails()) { + if ($bdate->isOK() && $person->canShow()) { $this->people[] = $person; } } @@ -424,7 +424,7 @@ class WT_Controller_Lifespan extends WT_Controller_Page { $evntwdth = $eventwidth."%"; //-- if the fact is a generic EVENt then get the qualifying TYPE if ($fact=="EVEN") { - $fact = $val->getType(); + $fact = $val->getAttribute('TYPE'); } $place = $val->getPlace(); $trans = WT_Gedcom_Tag::getLabel($fact); @@ -532,18 +532,18 @@ class WT_Controller_Lifespan extends WT_Controller_Page { $endjd =WT_Date_Gregorian::YMDtoJD($endyear+1, 1, 1)-1; $sql= - "SELECT DISTINCT 'INDI' AS type, i_id AS xref, i_file AS ged_id, i_gedcom AS gedrec". + "SELECT DISTINCT i_id AS xref, i_file AS gedcom_id, i_gedcom AS gedcom". " FROM `##individuals`". " JOIN `##dates` ON i_id=d_gid AND i_file=d_file". " WHERE i_file=? AND d_julianday1 BETWEEN ? AND ?"; $rows=WT_DB::prepare($sql) ->execute(array(WT_GED_ID, $startjd, $endjd)) - ->fetchAll(PDO::FETCH_ASSOC); + ->fetchAll(); $list=array(); foreach ($rows as $row) { - $list[]=WT_Person::getInstance($row); + $list[]=WT_Individual::getInstance($row->xref, $row->gedcom_id, $row->gedcom); } return $list; } diff --git a/library/WT/Controller/Media.php b/library/WT/Controller/Media.php index 562bf8aabe..c9fbf39547 100644 --- a/library/WT/Controller/Media.php +++ b/library/WT/Controller/Media.php @@ -2,7 +2,7 @@ // Controller for the media page // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -34,35 +34,8 @@ require_once WT_ROOT.'includes/functions/functions_import.php'; class WT_Controller_Media extends WT_Controller_GedcomRecord { public function __construct() { - $xref = safe_GET_xref('mid'); - - $gedrec=find_media_record($xref, WT_GED_ID); - if (WT_USER_CAN_EDIT) { - $newrec=find_updated_record($xref, WT_GED_ID); - } else { - $newrec=null; - } - - if ($gedrec===null) { - if ($newrec===null) { - // Nothing to see here. - parent::__construct(); - return; - } else { - // Create a dummy record from the first line of the new record. - // We need it for diffMerge(), getXref(), etc. - list($gedrec)=explode("\n", $newrec); - } - } - - $this->record = new WT_Media($gedrec); - - // If there are pending changes, merge them in. - if ($newrec!==null) { - $diff_record=new WT_Media($newrec); - $diff_record->setChanged(true); - $this->record->diffMerge($diff_record); - } + $xref = safe_GET_xref('mid'); + $this->record = WT_Media::getInstance($xref); parent::__construct(); } @@ -73,7 +46,7 @@ class WT_Controller_Media extends WT_Controller_GedcomRecord { function getEditMenu() { $SHOW_GEDCOM_RECORD=get_gedcom_setting(WT_GED_ID, 'SHOW_GEDCOM_RECORD'); - if (!$this->record || $this->record->isMarkedDeleted()) { + if (!$this->record || $this->record->isOld()) { return null; } @@ -107,21 +80,6 @@ class WT_Controller_Media extends WT_Controller_GedcomRecord { $menu->addSubmenu($submenu); } - // edit/view raw gedcom - if (WT_USER_IS_ADMIN || $SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('Edit raw GEDCOM record'), '#', 'menu-obje-editraw'); - $submenu->addOnclick("return edit_raw('".$this->record->getXref()."');"); - $menu->addSubmenu($submenu); - } elseif ($SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('View GEDCOM Record'), '#', 'menu-obje-viewraw'); - if (WT_USER_CAN_EDIT || WT_USER_CAN_ACCEPT) { - $submenu->addOnclick("return show_gedcom_record('new');"); - } else { - $submenu->addOnclick("return show_gedcom_record();"); - } - $menu->addSubmenu($submenu); - } - // delete if (WT_USER_CAN_EDIT) { $submenu = new WT_Menu(WT_I18N::translate('Delete'), '#', 'menu-obje-del'); @@ -153,17 +111,17 @@ class WT_Controller_Media extends WT_Controller_GedcomRecord { * @return array */ function getFacts($includeFileName=true) { - $facts = $this->record->getFacts(array()); + $facts = $this->record->getFacts(); // Add some dummy facts to show additional information if ($this->record->fileExists()) { // get height and width of image, when available $imgsize = $this->record->getImageAttributes(); if (!empty($imgsize['WxH'])) { - $facts[] = new WT_Event('1 __IMAGE_SIZE__ '.$imgsize['WxH'], $this->record, 0); + $facts[] = new WT_Fact('1 __IMAGE_SIZE__ '.$imgsize['WxH'], $this->record, 0); } //Prints the file size - $facts[] = new WT_Event('1 __FILE_SIZE__ '.$this->record->getFilesize(), $this->record, 0); + $facts[] = new WT_Fact('1 __FILE_SIZE__ '.$this->record->getFilesize(), $this->record, 0); } sort_facts($facts); diff --git a/library/WT/Controller/Note.php b/library/WT/Controller/Note.php index 01fad484c8..003fd45acd 100644 --- a/library/WT/Controller/Note.php +++ b/library/WT/Controller/Note.php @@ -2,7 +2,7 @@ // Controller for the shared note page // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2009 PGV Development Team. All rights reserved. @@ -33,35 +33,8 @@ require_once WT_ROOT.'includes/functions/functions_import.php'; class WT_Controller_Note extends WT_Controller_GedcomRecord { public function __construct() { - $xref=safe_GET_xref('nid'); - - $gedrec=find_other_record($xref, WT_GED_ID); - if (WT_USER_CAN_EDIT) { - $newrec=find_updated_record($xref, WT_GED_ID); - } else { - $newrec=null; - } - - if ($gedrec===null) { - if ($newrec===null) { - // Nothing to see here. - parent::__construct(); - return; - } else { - // Create a dummy record from the first line of the new record. - // We need it for diffMerge(), getXref(), etc. - list($gedrec)=explode("\n", $newrec); - } - } - - $this->record = new WT_Note($gedrec); - - // If there are pending changes, merge them in. - if ($newrec!==null) { - $diff_record=new WT_Note($newrec); - $diff_record->setChanged(true); - $this->record->diffMerge($diff_record); - } + $xref = safe_GET_xref('nid'); + $this->record = WT_Note::getInstance($xref); parent::__construct(); } @@ -72,7 +45,7 @@ class WT_Controller_Note extends WT_Controller_GedcomRecord { function getEditMenu() { $SHOW_GEDCOM_RECORD=get_gedcom_setting(WT_GED_ID, 'SHOW_GEDCOM_RECORD'); - if (!$this->record || $this->record->isMarkedDeleted()) { + if (!$this->record || $this->record->isOld()) { return null; } @@ -85,21 +58,6 @@ class WT_Controller_Note extends WT_Controller_GedcomRecord { $menu->addSubmenu($submenu); } - // edit/view raw gedcom - if (WT_USER_IS_ADMIN || $SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('Edit raw GEDCOM record'), '#', 'menu-note-editraw'); - $submenu->addOnclick("return edit_raw('".$this->record->getXref()."');"); - $menu->addSubmenu($submenu); - } elseif ($SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('View GEDCOM Record'), '#', 'menu-note-viewraw'); - if (WT_USER_CAN_EDIT) { - $submenu->addOnclick("return show_gedcom_record('new');"); - } else { - $submenu->addOnclick("return show_gedcom_record();"); - } - $menu->addSubmenu($submenu); - } - // delete if (WT_USER_CAN_EDIT) { $submenu = new WT_Menu(WT_I18N::translate('Delete'), '#', 'menu-note-del'); diff --git a/library/WT/Controller/Page.php b/library/WT/Controller/Page.php index 55a6b55cdc..d2fe6031db 100644 --- a/library/WT/Controller/Page.php +++ b/library/WT/Controller/Page.php @@ -163,7 +163,6 @@ class WT_Controller_Page extends WT_Controller_Base { var browserType = "'.$BROWSERTYPE.'"; var WT_SCRIPT_NAME = "'.WT_SCRIPT_NAME.'"; var WT_LOCALE = "'.WT_LOCALE.'"; - var accesstime = '.WT_TIMESTAMP.'; ', self::JS_PRIORITY_HIGH); // Temporary fix for access to main menu hover elements on android/blackberry touch devices @@ -220,16 +219,16 @@ class WT_Controller_Page extends WT_Controller_Base { static $individual; // Only query the DB once. if (!$individual && WT_USER_ROOT_ID) { - $individual=WT_Person::getInstance(WT_USER_ROOT_ID); + $individual=WT_Individual::getInstance(WT_USER_ROOT_ID); } if (!$individual && WT_USER_GEDCOM_ID) { - $individual=WT_Person::getInstance(WT_USER_GEDCOM_ID); + $individual=WT_Individual::getInstance(WT_USER_GEDCOM_ID); } if (!$individual) { - $individual=WT_Person::getInstance(get_gedcom_setting(WT_GED_ID, 'PEDIGREE_ROOT_ID')); + $individual=WT_Individual::getInstance(get_gedcom_setting(WT_GED_ID, 'PEDIGREE_ROOT_ID')); } if (!$individual) { - $individual=WT_Person::getInstance( + $individual=WT_Individual::getInstance( WT_DB::prepare( "SELECT MIN(i_id) FROM `##individuals` WHERE i_file=?" )->execute(array(WT_GED_ID))->fetchOne() @@ -237,7 +236,7 @@ class WT_Controller_Page extends WT_Controller_Base { } if (!$individual) { // always return a record - $individual=new WT_Person('0 @I@ INDI'); + $individual=new WT_Individual('I', '0 @I@ INDI', null, WT_GED_ID); } return $individual; } @@ -252,7 +251,7 @@ class WT_Controller_Page extends WT_Controller_Base { } } // always return a record - return new WT_Family('0 @F@ FAM'); + return new WT_Family('F', '0 @F@ FAM', null, WT_GED_ID); } public function getSignificantSurname() { return ''; diff --git a/library/WT/Controller/Pedigree.php b/library/WT/Controller/Pedigree.php index 65bb3174f0..5133aea959 100644 --- a/library/WT/Controller/Pedigree.php +++ b/library/WT/Controller/Pedigree.php @@ -2,7 +2,7 @@ // Controller for the pedigree chart // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. @@ -85,7 +85,7 @@ class WT_Controller_Pedigree extends WT_Controller_Chart { $show_full = $this->show_full; $talloffset = $this->talloffset; - if ($this->root && $this->root->canDisplayName()) { + if ($this->root && $this->root->canShowName()) { $this->setPageTitle( /* I18N: %s is an individual’s name */ WT_I18N::translate('Pedigree tree of %s', $this->root->getFullName()) diff --git a/library/WT/Controller/Repository.php b/library/WT/Controller/Repository.php index 7af4c8c139..2c409a6110 100644 --- a/library/WT/Controller/Repository.php +++ b/library/WT/Controller/Repository.php @@ -2,7 +2,7 @@ // Controller for the repository page // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2010 PGV Development Team. All rights reserved. @@ -33,35 +33,8 @@ require_once WT_ROOT.'includes/functions/functions_import.php'; class WT_Controller_Repository extends WT_Controller_GedcomRecord { public function __construct() { - $xref = safe_GET_xref('rid'); - - $gedrec=find_other_record($xref, WT_GED_ID); - if (WT_USER_CAN_EDIT) { - $newrec=find_updated_record($xref, WT_GED_ID); - } else { - $newrec=null; - } - - if ($gedrec===null) { - if ($newrec===null) { - // Nothing to see here. - parent::__construct(); - return; - } else { - // Create a dummy record from the first line of the new record. - // We need it for diffMerge(), getXref(), etc. - list($gedrec)=explode("\n", $newrec); - } - } - - $this->record = new WT_Repository($gedrec); - - // If there are pending changes, merge them in. - if ($newrec!==null) { - $diff_record=new WT_Repository($newrec); - $diff_record->setChanged(true); - $this->record->diffMerge($diff_record); - } + $xref = safe_GET_xref('rid'); + $this->record = WT_Repository::getInstance($xref); parent::__construct(); } @@ -72,7 +45,7 @@ class WT_Controller_Repository extends WT_Controller_GedcomRecord { function getEditMenu() { $SHOW_GEDCOM_RECORD=get_gedcom_setting(WT_GED_ID, 'SHOW_GEDCOM_RECORD'); - if (!$this->record || $this->record->isMarkedDeleted()) { + if (!$this->record || $this->record->isOld()) { return null; } @@ -85,21 +58,6 @@ class WT_Controller_Repository extends WT_Controller_GedcomRecord { $menu->addSubmenu($submenu); } - // edit/view raw gedcom - if (WT_USER_IS_ADMIN || $SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('Edit raw GEDCOM record'), '#', 'menu-repo-editraw'); - $submenu->addOnclick("return edit_raw('".$this->record->getXref()."');"); - $menu->addSubmenu($submenu); - } elseif ($SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('View GEDCOM Record'), '#', 'menu-repo-viewraw'); - if (WT_USER_CAN_EDIT) { - $submenu->addOnclick("return show_gedcom_record('new');"); - } else { - $submenu->addOnclick("return show_gedcom_record();"); - } - $menu->addSubmenu($submenu); - } - // delete if (WT_USER_CAN_EDIT) { $submenu = new WT_Menu(WT_I18N::translate('Delete'), '#', 'menu-repo-del'); diff --git a/library/WT/Controller/Search.php b/library/WT/Controller/Search.php index 90a83891a1..ac67d6a0c7 100644 --- a/library/WT/Controller/Search.php +++ b/library/WT/Controller/Search.php @@ -285,7 +285,7 @@ class WT_Controller_Search extends WT_Controller_Page { // Then see if an ID is typed in. If so, we might want to jump there. if (isset ($this->query)) { $record=WT_GedcomRecord::getInstance($this->query); - if ($record && $record->canDisplayDetails()) { + if ($record && $record->canShow()) { header('Location: '.WT_SERVER_NAME.WT_SCRIPT_PATH.$record->getRawUrl()); exit; } @@ -357,7 +357,7 @@ class WT_Controller_Search extends WT_Controller_Page { // If ID cannot be displayed, continue to the search page. if (count($this->myindilist)==1 && !$this->myfamlist && !$this->mysourcelist && !$this->mynotelist) { $indi=$this->myindilist[0]; - if ($indi->canDisplayName()) { + if ($indi->canShowName()) { Zend_Session::writeClose(); header('Location: '.WT_SERVER_NAME.WT_SCRIPT_PATH.$indi->getRawUrl()); exit; @@ -365,7 +365,7 @@ class WT_Controller_Search extends WT_Controller_Page { } if (!$this->myindilist && count($this->myfamlist)==1 && !$this->mysourcelist && !$this->mynotelist) { $fam=$this->myfamlist[0]; - if ($fam->canDisplayName()) { + if ($fam->canShowName()) { Zend_Session::writeClose(); header('Location: '.WT_SERVER_NAME.WT_SCRIPT_PATH.$fam->getRawUrl()); exit; @@ -373,7 +373,7 @@ class WT_Controller_Search extends WT_Controller_Page { } if (!$this->myindilist && !$this->myfamlist && count($this->mysourcelist)==1 && !$this->mynotelist) { $sour=$this->mysourcelist[0]; - if ($sour->canDisplayName()) { + if ($sour->canShowName()) { Zend_Session::writeClose(); header('Location: '.WT_SERVER_NAME.WT_SCRIPT_PATH.$sour->getRawUrl()); exit; @@ -381,7 +381,7 @@ class WT_Controller_Search extends WT_Controller_Page { } if (!$this->myindilist && !$this->myfamlist && !$this->mysourcelist && count($this->mynotelist)==1) { $note=$this->mynotelist[0]; - if ($note->canDisplayName()) { + if ($note->canShowName()) { Zend_Session::writeClose(); header('Location: '.WT_SERVER_NAME.WT_SCRIPT_PATH.$note->getRawUrl()); exit; @@ -443,7 +443,7 @@ class WT_Controller_Search extends WT_Controller_Page { } //-- if the record changed replace the record otherwise remove it from the search results if ($newRecord != $oldRecord) { - replace_gedrec($individual->getXref(), WT_GED_ID, $newRecord); + $individual->updateRecord($newRecord, true); } else { unset($this->myindilist[$id]); } @@ -465,7 +465,7 @@ class WT_Controller_Search extends WT_Controller_Page { } //-- if the record changed replace the record otherwise remove it from the search results if ($newRecord != $oldRecord) { - replace_gedrec($family->getXref(), WT_GED_ID, $newRecord); + $family->updateRecord($newRecord, true); } else { unset($this->myfamlist[$id]); } @@ -490,7 +490,7 @@ class WT_Controller_Search extends WT_Controller_Page { } //-- if the record changed replace the record otherwise remove it from the search results if ($newRecord != $oldRecord) { - replace_gedrec($source->getXref(), WT_GED_ID, $newRecord); + $source->updateRecord($newRecord, true); } else { unset($this->mysourcelist[$id]); } @@ -506,7 +506,7 @@ class WT_Controller_Search extends WT_Controller_Page { } //-- if the record changed replace the record otherwise remove it from the search results if ($newRecord != $oldRecord) { - replace_gedrec($note->getXref(), WT_GED_ID, $newRecord); + $noteource->updateRecord($newRecord, true); } else { unset($this->mynotelist[$id]); } @@ -557,10 +557,10 @@ class WT_Controller_Search extends WT_Controller_Page { if ($this->showasso == "on") { foreach ($this->myindilist as $indi) { - foreach (fetch_linked_indi($indi->getXref(), 'ASSO', $indi->getGedId()) as $asso) { + foreach (fetch_linked_indi($indi->getXref(), 'ASSO', $indi->getGedcomId()) as $asso) { $this->myindilist[]=$asso; } - foreach (fetch_linked_fam($indi->getXref(), 'ASSO', $indi->getGedId()) as $asso) { + foreach (fetch_linked_fam($indi->getXref(), 'ASSO', $indi->getGedcomId()) as $asso) { $this->myfamlist[]=$asso; } } @@ -603,7 +603,7 @@ class WT_Controller_Search extends WT_Controller_Page { foreach ($this->sgeds as $ged_id=>$gedcom) { $datalist = array(); foreach ($this->myindilist as $individual) { - if ($individual->getGedId()==$ged_id) { + if ($individual->getGedcomId()==$ged_id) { $datalist[]=$individual; } } @@ -627,7 +627,7 @@ class WT_Controller_Search extends WT_Controller_Page { foreach ($this->sgeds as $ged_id=>$gedcom) { $datalist = array(); foreach ($this->myfamlist as $family) { - if ($family->getGedId()==$ged_id) { + if ($family->getGedcomId()==$ged_id) { $datalist[]=$family; } } @@ -651,7 +651,7 @@ class WT_Controller_Search extends WT_Controller_Page { foreach ($this->sgeds as $ged_id=>$gedcom) { $datalist = array(); foreach ($this->mysourcelist as $source) { - if ($source->getGedId()==$ged_id) { + if ($source->getGedcomId()==$ged_id) { $datalist[]=$source; } } @@ -675,7 +675,7 @@ class WT_Controller_Search extends WT_Controller_Page { foreach ($this->sgeds as $ged_id=>$gedcom) { $datalist = array(); foreach ($this->mynotelist as $note) { - if ($note->getGedId()==$ged_id) { + if ($note->getGedcomId()==$ged_id) { $datalist[]=$note; } } diff --git a/library/WT/Controller/Source.php b/library/WT/Controller/Source.php index f2825493d4..dff23a7558 100644 --- a/library/WT/Controller/Source.php +++ b/library/WT/Controller/Source.php @@ -2,7 +2,7 @@ // Controller for the source page // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2010 PGV Development Team. All rights reserved. @@ -33,35 +33,8 @@ require_once WT_ROOT.'includes/functions/functions_import.php'; class WT_Controller_Source extends WT_Controller_GedcomRecord { public function __construct() { - $xref=safe_GET_xref('sid'); - - $gedrec=find_source_record($xref, WT_GED_ID); - if (WT_USER_CAN_EDIT) { - $newrec=find_updated_record($xref, WT_GED_ID); - } else { - $newrec=null; - } - - if ($gedrec===null) { - if ($newrec===null) { - // Nothing to see here. - parent::__construct(); - return; - } else { - // Create a dummy record from the first line of the new record. - // We need it for diffMerge(), getXref(), etc. - list($gedrec)=explode("\n", $newrec); - } - } - - $this->record = new WT_Source($gedrec); - - // If there are pending changes, merge them in. - if ($newrec!==null) { - $diff_record=new WT_Source($newrec); - $diff_record->setChanged(true); - $this->record->diffMerge($diff_record); - } + $xref = safe_GET_xref('sid'); + $this->record = WT_Source::getInstance($xref); parent::__construct(); } @@ -72,7 +45,7 @@ class WT_Controller_Source extends WT_Controller_GedcomRecord { function getEditMenu() { $SHOW_GEDCOM_RECORD=get_gedcom_setting(WT_GED_ID, 'SHOW_GEDCOM_RECORD'); - if (!$this->record || $this->record->isMarkedDeleted()) { + if (!$this->record || $this->record->isOld()) { return null; } @@ -85,21 +58,6 @@ class WT_Controller_Source extends WT_Controller_GedcomRecord { $menu->addSubmenu($submenu); } - // edit/view raw gedcom - if (WT_USER_IS_ADMIN || $SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('Edit raw GEDCOM record'), '#', 'menu-sour-editraw'); - $submenu->addOnclick("return edit_raw('".$this->record->getXref()."');"); - $menu->addSubmenu($submenu); - } elseif ($SHOW_GEDCOM_RECORD) { - $submenu = new WT_Menu(WT_I18N::translate('View GEDCOM Record'), '#', 'menu-sour-viewraw'); - if (WT_USER_CAN_EDIT) { - $submenu->addOnclick("return show_gedcom_record('new');"); - } else { - $submenu->addOnclick("return show_gedcom_record();"); - } - $menu->addSubmenu($submenu); - } - // delete if (WT_USER_CAN_EDIT) { $submenu = new WT_Menu(WT_I18N::translate('Delete'), '#', 'menu-sour-del'); diff --git a/library/WT/Controller/Timeline.php b/library/WT/Controller/Timeline.php index 85f82ea2f7..349a843561 100644 --- a/library/WT/Controller/Timeline.php +++ b/library/WT/Controller/Timeline.php @@ -71,7 +71,7 @@ class WT_Controller_Timeline extends WT_Controller_Page { foreach ($this->pids as $value) { if ($value!=$remove) { $newpids[] = $value; - $person = WT_Person::getInstance($value); + $person = WT_Individual::getInstance($value); if ($person) { $this->people[] = $person; } @@ -81,7 +81,7 @@ class WT_Controller_Timeline extends WT_Controller_Page { $this->pidlinks = ""; /* @var $indi Person */ foreach ($this->people as $p=>$indi) { - if (!is_null($indi) && $indi->canDisplayDetails()) { + if (!is_null($indi) && $indi->canShow()) { //-- setup string of valid pids for links $this->pidlinks .= "pids%5B%5D=".$indi->getXref()."&"; $bdate = $indi->getBirthDate(); @@ -95,8 +95,14 @@ class WT_Controller_Timeline extends WT_Controller_Page { } } // find all the fact information - $indi->add_family_facts(false); - foreach ($indi->getIndiFacts() as $event) { + $facts = $indi->getFacts(); + foreach ($indi->getSpouseFamilies() as $family) { + foreach ($family->getFacts() as $fact) { + $fact->spouse = $family->getSpouse($indi); + $facts[] = $fact; + } + } + foreach ($facts as $event) { //-- get the fact type $fact = $event->getTag(); if (!in_array($fact, $this->nonfacts)) { @@ -137,8 +143,8 @@ class WT_Controller_Timeline extends WT_Controller_Page { $printed = false; for ($i=0; $i<count($this->people); $i++) { if (!is_null($this->people[$i])) { - if (!$this->people[$i]->canDisplayDetails()) { - if ($this->people[$i]->canDisplayName()) { + if (!$this->people[$i]->canShow()) { + if ($this->people[$i]->canShowName()) { echo " <a href=\"".$this->people[$i]->getHtmlUrl()."\">".$this->people[$i]->getFullName()."</a>"; print_privacy_error(); echo "<br>"; @@ -153,15 +159,15 @@ class WT_Controller_Timeline extends WT_Controller_Page { } } - function print_time_fact(WT_Event $event) { + function print_time_fact(WT_Fact $event) { global $basexoffset, $baseyoffset, $factcount, $TEXT_DIRECTION, $WT_IMAGES, $SHOW_PEDIGREE_PLACES, $placements; /* @var $event Event */ - $factrec = $event->getGedComRecord(); + $factrec = $event->getGedcom(); $fact = $event->getTag(); - $desc = $event->getDetail(); + $desc = $event->getValue(); if ($fact=="EVEN" || $fact=="FACT") { - $fact = $event->getType(); + $fact = $event->getAttribute('TYPE'); } //-- check if this is a family fact $gdate=$event->getDate(); @@ -201,11 +207,11 @@ class WT_Controller_Timeline extends WT_Controller_Page { echo ": 3px;\">"; $col = $event->temp % 6; echo "</td><td valign=\"top\" class=\"person".$col."\">"; - if (count($this->pids) > 6) echo $event->getParentObject()->getFullName()." - "; - $record=$event->getParentObject(); + if (count($this->pids) > 6) echo $event->getParent()->getFullName()." - "; + $record=$event->getParent(); echo $event->getLabel(); echo " -- "; - if ($record instanceof WT_Person) { + if ($record instanceof WT_Individual) { echo format_fact_date($event, $record, false, false); } elseif ($record instanceof WT_Family) { echo $gdate->Display(false); @@ -242,7 +248,11 @@ class WT_Controller_Timeline extends WT_Controller_Page { } } //-- print spouse name for marriage events - $spouse = $event->getSpouse(); + if (isset($event->spouse)) { + $spouse = $event->spouse; + } else { + $spouse = null; + } if ($spouse) { for ($p=0; $p<count($this->pids); $p++) { if ($this->pids[$p]==$spouse->getXref()) break; @@ -252,7 +262,7 @@ class WT_Controller_Timeline extends WT_Controller_Page { if ($spouse->getXref()!=$this->pids[$p]) { echo ' <a href="', $spouse->getHtmlUrl(), '">', $spouse->getFullName(), '</a>'; } else { - echo ' <a href="', $event->getParentObject()->getHtmlUrl(), '">', $event->getParentObject()->getFullName(), '</a>'; + echo ' <a href="', $event->getParent()->getHtmlUrl(), '">', $event->getParent()->getFullName(), '</a>'; } } echo "</td></tr></table>"; @@ -284,7 +294,7 @@ class WT_Controller_Timeline extends WT_Controller_Page { public function getSignificantIndividual() { if ($this->pids) { - return WT_Person::getInstance($this->pids[0]); + return WT_Individual::getInstance($this->pids[0]); } else { return parent::getSignificantIndividual(); } diff --git a/library/WT/Event.php b/library/WT/Fact.php index da786f8d86..8655c76fef 100644 --- a/library/WT/Event.php +++ b/library/WT/Fact.php @@ -2,7 +2,7 @@ // Class that defines an event details object // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2008 PGV Development Team. All rights reserved. @@ -28,109 +28,83 @@ if (!defined('WT_WEBTREES')) { exit; } -class WT_Event { -// These objects need further refinement in their implementations and parsing -// var $address = null; -// var $notes = array(); //[0..*]: string -// var $sourceCitations = array(); //[0..*]: SourceCitation -// var $multimediaLinks = array(); //[0..*]: MultimediaLink +class WT_Fact { + private $fact_id = null; // Unique identifier for this fact + private $parent = null; // The GEDCOM record from which this fact is taken. + private $gedcom = null; // The raw GEDCOM data for this fact + private $tag = null; // The GEDCOM tag for this record - var $lineNumber = null; - var $canShow = null; - var $state = ""; - var $type = NULL; - var $tag = NULL; - var $date = NULL; - var $place = null; - var $gedcomRecord = null; - var $resn = null; - var $dest = false; - var $label = null; - var $parentObject = null; - var $detail = NULL; - var $values = NULL; - var $sortOrder = 0; - var $sortDate = NULL; - //-- temporary state variable that can be used by other scripts - var $temp = NULL; + private $is_old = false; // Is this a pending record? + private $is_new = false; // Is this a pending record? - // Is this an old/new pending record? - private $is_old = false; - private $is_new = false; + private $date = null; // The WT_Date object for the "2 DATE ..." attribute + private $place = null; // The WT_Place object for the "2 PLAC ..." attribute - // For family facts on individual pages - who is the significant spouse. - private $spouse; + // Temporary(!) variables that are used by other scripts + public $temp = null; // Timeline controller + public $sortOrder = 0; // sort_facts() - /** - * Get the value for the first given GEDCOM tag - * - * @param string $code - * @return string - */ - function getValue($code) { - if (is_null($this->values)) { - $this->values=array(); - preg_match_all('/\n2 ('.WT_REGEX_TAG.') (.+)/', $this->gedcomRecord, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - // If this is a link, remove the "@" - if (preg_match('/^@'.WT_REGEX_XREF.'@$/', $match[2])) { - $this->values[$match[1]]=trim($match[2], "@"); - } else { - $this->values[$match[1]]=$match[2]; - } - } - } - if (array_key_exists($code, $this->values)) { - return $this->values[$code]; + // Create an event objects from a gedcom fragment. + // We need the parent object (to check privacy) and a (pseudo) fact ID to + // identify the fact within the record. + function __construct($gedcom, WT_GedcomRecord $parent, $fact_id) { + if (preg_match('/^1 ('.WT_REGEX_TAG.')/', $gedcom, $match)) { + $this->gedcom = $gedcom; + $this->parent = $parent; + $this->fact_id = $fact_id; + $this->tag = $match[1]; + } else { + // TODO need to rewrite code that passes dummy data to this function + //throw new Exception('Invalid GEDCOM data passed to WT_Fact::_construct('.$gedcom.')'); } - return null; } - // Create an event objects from a gedcom fragment. - // We also need to know the parent (to check privacy, etc.) and - // the line number (from the original, privacy-filtered) gedcom - // record, to allow editing - function __construct($subrecord, $parent, $lineNumber) { - if (preg_match('/^1 ('.WT_REGEX_TAG.') ?(.*)((\n2 CONT.*)*)/', $subrecord, $match)) { - $this->tag =$match[1]; - $this->detail=$match[2]; - // Some detail records contain multiple lines - if ($match[3]) { - $this->detail.=str_replace(array("\n2 CONT ", "\n2 CONT"), "\n", $match[3]); - } + // Get the value of level 1 data in the fact + // Allow for multi-line values + function getValue() { + if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT .*)*))/', $this->gedcom, $match)) { + return str_replace("\n2 CONT ", "\n", $match[1]); } else { - // We are not ready for this yet. - // throw new Exception('Invalid GEDCOM data passed to WT_Event::_construct('.$subrecord.')'); + return null; } - $this->gedcomRecord=$subrecord; - $this->parentObject=$parent; - $this->lineNumber =$lineNumber; } - function setState($s) { - $this->state = $s; + // Get the record to which this fact links + function getTarget() { + $xref = trim($this->getValue(), '@'); + switch ($this->tag) { + case 'FAMC': + case 'FAMS': + return WT_Family::getInstance($xref); + case 'HUSB': + case 'WIFE': + case 'CHIL': + return WT_Individual::getInstance($xref); + case 'SOUR': + return WT_Source::getInstance($xref); + case 'OBJE': + return WT_Media::getInstance($xref); + case 'REPO': + return WT_Repository::getInstance($xref); + case 'NOTE': + return WT_Note::getInstance($xref); + default: + return WT_GedcomRecord::getInstance($xref); + } } - function getState() { - return $this->state; + // Get the value of level 2 data in the fact + function getAttribute($tag) { + if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT .*)*)*)/', $this->gedcom, $match)) { + return str_replace("\n2 CONT ", "\n", $match[1]); + } else { + return null; + } } - /** - * Check whether or not this event can be shown - * - * @return boolean - */ + // Do the privacy rules allow us to display this fact to the current user function canShow() { - if (is_null($this->canShow)) { - if (empty($this->gedcomRecord)) { - $this->canShow = false; - } elseif (!is_null($this->parentObject)) { - $this->canShow = canDisplayFact($this->parentObject->getXref(), $this->parentObject->getGedId(), $this->gedcomRecord); - } else { - $this->canShow = true; - } - } - return $this->canShow; + return canDisplayFact($this->parent->getXref(), $this->parent->getGedcomId(), $this->gedcom); } // Check whether this fact is protected against edit @@ -138,92 +112,52 @@ class WT_Event { // Managers can edit anything // Members cannot edit RESN, CHAN and locked records return - $this->parentObject && $this->parentObject->canEdit() && ( + $this->parent->canEdit() && !$this->isOld() && ( WT_USER_GEDCOM_ADMIN || - WT_USER_CAN_EDIT && strpos($this->gedcomRecord, "\n2 RESN locked")===false && $this->getTag()!='RESN' && $this->getTag()!='CHAN' + WT_USER_CAN_EDIT && strpos($this->gedcom, "\n2 RESN locked")===false && $this->getTag()!='RESN' && $this->getTag()!='CHAN' ); } - /** - * The 4 character event type specified by GEDCom. - * - * @return string - */ - function getType() { - if (is_null($this->type)) - $this->type=$this->getValue('TYPE'); - return $this->type; - } - - /** - * The place where the event occured. - * - * @return string - */ + // The place where the event occured. function getPlace() { - if (is_null($this->place)) { - $this->place=$this->getValue('PLAC'); + if ($this->place === null) { + $this->place = $this->getAttribute('PLAC'); } return $this->place; } - // For family facts on individual pages, we need to know the spouse - public function setSpouse(WT_Person $spouse=null) { - $this->spouse=$spouse; - } - public function getSpouse() { - return $this->spouse; - } - // We can call this function many times, especially when sorting, // so keep a copy of the date. function getDate() { - if ($this->date===null) { - $this->date=new WT_Date($this->getValue('DATE')); + if ($this->date === null) { + $this->date = new WT_Date($this->getAttribute('DATE')); } - return $this->date; } - /** - * The remaining unparsed GEDCom record - * - * @return string - */ - function getGedcomRecord() { - return $this->gedcomRecord; + // The raw GEDCOM data for this fact + function getGedcom() { + return $this->gedcom; } - /** - * The line number, or line of occurrence in the GEDCom record. - * - * @return unknown - */ - function getLineNumber() { - return $this->lineNumber; + // Unique identifier for the fact + function getFactId() { + return $this->fact_id; } - /** - * - */ + // What sort of fact is this? function getTag() { return $this->tag; } - /** - * The Person/Family record where this WT_Event came from - * - * @return GedcomRecord - */ - function getParentObject() { - return $this->parentObject; + // Used to convert a real fact (e.g. BIRT) into a close-relative’s fact (e.g. _BIRT_CHIL) + function setTag($tag) { + $this->tag = $tag; } - /** - * - */ - function getDetail() { - return $this->detail; + // The Person/Family record where this WT_Fact came from + function getParent() { + return $this->parent; } function getLabel($abbreviate=false) { @@ -239,42 +173,39 @@ class WT_Event { } // no break - drop into next case default: - return WT_Gedcom_Tag::getLabel($this->tag, $this->parentObject); + return WT_Gedcom_Tag::getLabel($this->tag, $this->parent); } } } + // Is this a pending edit? public function setIsOld() { - $this->is_old=true; - $this->is_new=false; + $this->is_old = true; + $this->is_new = false; } - public function getIsOld() { + public function isOld() { return $this->is_old; } public function setIsNew() { - $this->is_new=true; - $this->is_old=false; + $this->is_new = true; + $this->is_old = false; } - public function getIsNew() { + public function isNew() { return $this->is_new; } - /** - * Print a simple fact version of this event - * - * @param boolean $return whether to print or return - * @param boolean $anchor whether to add anchor to date and place - */ + // Print a simple fact version of this event function print_simple_fact($return=false, $anchor=false) { - global $SHOW_PEDIGREE_PLACES, $ABBREVIATE_CHART_LABELS; + global $ABBREVIATE_CHART_LABELS; + + $value = $this->getValue(); - if (!$this->canShow()) return ""; $data = '<span class="details_label">'.$this->getLabel($ABBREVIATE_CHART_LABELS).'</span>'; // Don't display "yes", because format_fact_date() does this for us. (Should it?) - if ($this->detail && $this->detail!='Y') { - $data .= ' <span dir="auto">'.htmlspecialchars($this->detail).'</span>'; + if ($value && $value != 'Y') { + $data .= ' <span dir="auto">' . htmlspecialchars($value) . '</span>'; } - $data .= ' '.format_fact_date($this, $this->getParentObject(), $anchor, false); + $data .= ' '.format_fact_date($this, $this->getParent(), $anchor, false); $data .= ' '.format_fact_place($this, $anchor, false, false); $data .= '<br>'; if ($return) { @@ -300,13 +231,7 @@ class WT_Event { } } - /** - * Static Helper functions to sort events - * - * @param WT_Event $a - * @param WT_Event $b - * @return int - */ + // Static Helper functions to sort events static function CompareDate($a, $b) { if ($a->getDate()->isOK() && $b->getDate()->isOK()) { // If both events have dates, compare by date @@ -326,13 +251,7 @@ class WT_Event { } } - /** - * Static method to Compare two events by their type - * - * @param WT_Event $a - * @param WT_Event $b - * @return int - */ + // Static method to Compare two events by their type static function CompareType($a, $b) { global $factsort; @@ -398,7 +317,7 @@ class WT_Event { // Facts from same families stay grouped together // Keep MARR and DIV from the same families from mixing with events from other FAMs // Use the original order in which the facts were added - if ($a->parentObject instanceof WT_Family && $b->parentObject instanceof WT_Family && !$a->parentObject->equals($b->parentObject)) { + if ($a->parent instanceof WT_Family && $b->parent instanceof WT_Family && !$a->parent->equals($b->parent)) { return $a->sortOrder - $b->sortOrder; } @@ -423,13 +342,13 @@ class WT_Event { //-- don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI //-- treat dated after BURI facts as BURI instead - if ($a->getValue('DATE')!=NULL && $factsort[$atag]>$factsort['BURI'] && $factsort[$atag]<$factsort['CHAN']) $atag='BURI'; - if ($b->getValue('DATE')!=NULL && $factsort[$btag]>$factsort['BURI'] && $factsort[$btag]<$factsort['CHAN']) $btag='BURI'; + if ($a->getAttribute('DATE')!=NULL && $factsort[$atag]>$factsort['BURI'] && $factsort[$atag]<$factsort['CHAN']) $atag='BURI'; + if ($b->getAttribute('DATE')!=NULL && $factsort[$btag]>$factsort['BURI'] && $factsort[$btag]<$factsort['CHAN']) $btag='BURI'; $ret = $factsort[$atag]-$factsort[$btag]; //-- if facts are the same then put dated facts before non-dated facts if ($ret==0) { - if ($a->getValue('DATE')!=NULL && $b->getValue('DATE')==NULL) return -1; - if ($b->getValue('DATE')!=NULL && $a->getValue('DATE')==NULL) return 1; + if ($a->getAttribute('DATE')!=NULL && $b->getAttribute('DATE')==NULL) return -1; + if ($b->getAttribute('DATE')!=NULL && $a->getAttribute('DATE')==NULL) return 1; //-- if no sorting preference, then keep original ordering $ret = $a->sortOrder - $b->sortOrder; } diff --git a/library/WT/Family.php b/library/WT/Family.php index cc7876451c..16193de7fa 100644 --- a/library/WT/Family.php +++ b/library/WT/Family.php @@ -30,6 +30,7 @@ if (!defined('WT_WEBTREES')) { class WT_Family extends WT_GedcomRecord { const RECORD_TYPE = 'FAM'; + const SQL_FETCH = "SELECT f_gedcom FROM `##families` WHERE f_id=? AND f_file=?"; const URL_PREFIX = 'family.php?famid='; private $husb = null; @@ -37,23 +38,15 @@ class WT_Family extends WT_GedcomRecord { private $marriage = null; // Create a Family object from either raw GEDCOM data or a database row - function __construct($data) { - if (is_array($data)) { - // Construct from a row from the database - if (preg_match('/^1 HUSB @(.+)@/m', $data['gedrec'], $match)) { - $this->husb=WT_Person::getInstance($match[1]); - } - if (preg_match('/^1 WIFE @(.+)@/m', $data['gedrec'], $match)) { - $this->wife=WT_Person::getInstance($match[1]); - } - } else { - // Construct from raw GEDCOM data - if (preg_match('/^1 HUSB @(.+)@/m', $data, $match)) { - $this->husb=WT_Person::getInstance($match[1]); - } - if (preg_match('/^1 WIFE @(.+)@/m', $data, $match)) { - $this->wife=WT_Person::getInstance($match[1]); - } + function __construct($xref, $gedcom, $pending, $gedcom_id) { + parent::__construct($xref, $gedcom, $pending, $gedcom_id); + + // TODO: fetch these from WT_Fact objects + if (preg_match('/^1 HUSB @(.+)@/m', $gedcom.$pending, $match)) { + $this->husb = WT_Individual::getInstance($match[1]); + } + if (preg_match('/^1 WIFE @(.+)@/m', $gedcom.$pending, $match)) { + $this->wife = WT_Individual::getInstance($match[1]); } // Make sure husb/wife are the right way round. @@ -61,7 +54,6 @@ class WT_Family extends WT_GedcomRecord { list($this->husb, $this->wife)=array($this->wife, $this->husb); } - parent::__construct($data); } // Generate a private version of this record @@ -70,10 +62,10 @@ class WT_Family extends WT_GedcomRecord { $rec='0 @'.$this->xref.'@ FAM'; // Just show the 1 CHIL/HUSB/WIFE tag, not any subtags, which may contain private data - preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @('.WT_REGEX_XREF.')@/', $this->_gedrec, $matches, PREG_SET_ORDER); + preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @('.WT_REGEX_XREF.')@/', $this->gedcom, $matches, PREG_SET_ORDER); foreach ($matches as $match) { - $rela=WT_Person::getInstance($match[1]); - if ($rela && ($SHOW_PRIVATE_RELATIONSHIPS || $rela->canDisplayDetails($access_level))) { + $rela=WT_Individual::getInstance($match[1]); + if ($rela && ($SHOW_PRIVATE_RELATIONSHIPS || $rela->canShow($access_level))) { $rec.=$match[0]; } } @@ -81,16 +73,14 @@ class WT_Family extends WT_GedcomRecord { } // Fetch the record from the database - protected static function fetchGedcomRecord($xref, $ged_id) { + protected static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; if ($statement===null) { - $statement=WT_DB::prepare( - "SELECT 'FAM' AS type, f_id AS xref, f_file AS ged_id, f_gedcom AS gedrec ". - "FROM `##families` WHERE f_id=? AND f_file=?" - ); + $statement=WT_DB::prepare("SELECT f_gedcom FROM `##families` WHERE f_id=? AND f_file=?"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } /** @@ -109,12 +99,12 @@ class WT_Family extends WT_GedcomRecord { } // Implement family-specific privacy logic - protected function _canDisplayDetailsByType($access_level) { + protected function _canShowByType($access_level) { // Hide a family if any member is private - preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @('.WT_REGEX_XREF.')@/', $this->_gedrec, $matches); + preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @('.WT_REGEX_XREF.')@/', $this->gedcom, $matches); foreach ($matches[1] as $match) { - $person=WT_Person::getInstance($match); - if ($person && !$person->canDisplayDetails($access_level)) { + $person=WT_Individual::getInstance($match); + if ($person && !$person->canShow($access_level)) { return false; } } @@ -122,7 +112,7 @@ class WT_Family extends WT_GedcomRecord { } // Can the name of this record be shown? - public function canDisplayName($access_level=WT_USER_ACCESS_LEVEL) { + public function canShowName($access_level=WT_USER_ACCESS_LEVEL) { // We can always see the name (Husband-name + Wife-name), however, // the name will often be "private + private" return true; @@ -137,10 +127,10 @@ class WT_Family extends WT_GedcomRecord { if (is_null($this->wife) || is_null($this->husb)) { return null; } - if ($this->wife->equals($person) && $this->husb->canDisplayDetails($access_level)) { + if ($this->wife->equals($person) && $this->husb->canShow($access_level)) { return $this->husb; } - if ($this->husb->equals($person) && $this->wife->canDisplayDetails($access_level)) { + if ($this->husb->equals($person) && $this->wife->canShow($access_level)) { return $this->wife; } return null; @@ -148,10 +138,10 @@ class WT_Family extends WT_GedcomRecord { function getSpouses($access_level=WT_USER_ACCESS_LEVEL) { $spouses=array(); - if ($this->husb && $this->husb->canDisplayDetails($access_level)) { + if ($this->husb && $this->husb->canShow($access_level)) { $spouses[]=$this->husb; } - if ($this->wife && $this->wife->canDisplayDetails($access_level)) { + if ($this->wife && $this->wife->canShow($access_level)) { $spouses[]=$this->wife; } return $spouses; @@ -165,10 +155,10 @@ class WT_Family extends WT_GedcomRecord { global $SHOW_PRIVATE_RELATIONSHIPS; $children=array(); - preg_match_all('/\n1 CHIL @('.WT_REGEX_XREF.')@/', $this->_gedrec, $match); + preg_match_all('/\n1 CHIL @('.WT_REGEX_XREF.')@/', $this->gedcom, $match); foreach ($match[1] as $pid) { - $child=WT_Person::getInstance($pid); - if ($child && ($SHOW_PRIVATE_RELATIONSHIPS || $child->canDisplayDetails($access_level))) { + $child=WT_Individual::getInstance($pid); + if ($child && ($SHOW_PRIVATE_RELATIONSHIPS || $child->canShow($access_level))) { $children[]=$child; } } @@ -186,69 +176,23 @@ class WT_Family extends WT_GedcomRecord { */ function getNumberOfChildren() { - $nchi1=(int)get_gedcom_value('NCHI', 1, $this->getGedcomRecord()); - $nchi2=(int)get_gedcom_value('NCHI', 2, $this->getGedcomRecord()); + $nchi1=(int)get_gedcom_value('NCHI', 1, $this->getGedcom()); + $nchi2=(int)get_gedcom_value('NCHI', 2, $this->getGedcom()); $nchi3=count($this->getChildren()); return max($nchi1, $nchi2, $nchi3); } /** - * get updated Family - * If there is an updated family record in the gedcom file - * return a new family object for it - */ - function getUpdatedFamily() { - if ($this->getChanged()) { - return $this; - } - if (WT_USER_CAN_EDIT && $this->canDisplayDetails()) { - $newrec = find_updated_record($this->xref, $this->ged_id); - if (!is_null($newrec)) { - $newfamily = new WT_Family($newrec); - $newfamily->setChanged(true); - return $newfamily; - } - } - return null; - } - /** - * check if this family has the given person - * as a parent in the family - * @param Person $person - */ - function hasParent($person) { - if (is_null($person)) return false; - if ($person->equals($this->husb)) return true; - if ($person->equals($this->wife)) return true; - return false; - } - /** - * check if this family has the given person - * as a child in the family - * @param Person $person - */ - function hasChild($person) { - if ($person) { - foreach ($this->getChildren() as $child) { - if ($person->equals($child)) { - return true; - } - } - } - return false; - } - - /** * parse marriage record */ function _parseMarriageRecord() { - $this->marriage = new WT_Event(get_sub_record(1, '1 MARR', $this->getGedcomRecord()), $this, 0); + $this->marriage = new WT_Fact(get_sub_record(1, '1 MARR', $this->getGedcom()), $this, 0); } /** * get the marriage event * - * @return WT_Event + * @return WT_Fact */ function getMarriage() { if (is_null($this->marriage)) $this->_parseMarriageRecord(); @@ -261,17 +205,17 @@ class WT_Family extends WT_GedcomRecord { */ function getMarriageRecord() { if (is_null($this->marriage)) $this->_parseMarriageRecord(); - return $this->marriage->getGedcomRecord(); + return $this->marriage->getGedcom(); } // Return whether or not this family ended in a divorce or was never married. // Note that this is calculated using unprivatized data, so we can // always distinguish spouses from ex-spouses. function isDivorced() { - return (bool)preg_match('/\n1 ('.WT_EVENTS_DIV.')( Y|\n)/', $this->_gedrec); + return (bool)preg_match('/\n1 ('.WT_EVENTS_DIV.')( Y|\n)/', $this->gedcom); } function isNotMarried() { - return (bool)preg_match('/\n1 _NMR( Y|\n)/', $this->_gedrec); + return (bool)preg_match('/\n1 _NMR( Y|\n)/', $this->gedcom); } /** @@ -279,7 +223,7 @@ class WT_Family extends WT_GedcomRecord { * @return string */ function getMarriageDate() { - if (!$this->canDisplayDetails()) { + if (!$this->canShow()) { return new WT_Date(''); } if (is_null($this->marriage)) { @@ -299,7 +243,7 @@ class WT_Family extends WT_GedcomRecord { */ function getMarriageType() { if (is_null($this->marriage)) $this->_parseMarriageRecord(); - return $this->marriage->getType(); + return $this->marriage->getAttribute('TYPE'); } /** @@ -313,7 +257,7 @@ class WT_Family extends WT_GedcomRecord { // Get all the dates/places for marriages - for the FAM lists function getAllMarriageDates() { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach (explode('|', WT_EVENTS_MARR) as $event) { if ($array=$this->getAllEventDates($event)) { return $array; @@ -323,7 +267,7 @@ class WT_Family extends WT_GedcomRecord { return array(); } function getAllMarriagePlaces() { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach (explode('|', WT_EVENTS_MARR) as $event) { if ($array=$this->getAllEventPlaces($event)) { return $array; @@ -336,8 +280,8 @@ class WT_Family extends WT_GedcomRecord { // Get an array of structures containing all the names in the record public function getAllNames() { if (is_null($this->_getAllNames)) { - $husb=$this->husb ? $this->husb : new WT_Person('1 SEX M'); - $wife=$this->wife ? $this->wife : new WT_Person('1 SEX F'); + $husb=$this->husb ? $this->husb : new WT_Individual('M', '1 SEX M', null, $this->gedcom_id); + $wife=$this->wife ? $this->wife : new WT_Individual('F', '1 SEX F', null, $this->gedcom_id); // Check the script used by each name, so we can match cyrillic with cyrillic, greek with greek, etc. $husb_names=$husb->getAllNames(); foreach ($husb_names as $n=>$husb_name) { diff --git a/library/WT/Gedcom/Code/Adop.php b/library/WT/Gedcom/Code/Adop.php index 5a1d9edd79..4b10b4dbcb 100644 --- a/library/WT/Gedcom/Code/Adop.php +++ b/library/WT/Gedcom/Code/Adop.php @@ -2,7 +2,7 @@ // Functions and logic for GEDCOM "PEDI" codes // // webtrees: Web based Family History software -// Copyright (C) 2011 webtrees development team. +// Copyright (C) 2013 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 @@ -31,7 +31,7 @@ class WT_Gedcom_Code_Adop { // Translate a code, for an (optional) record public static function getValue($type, $record=null) { - if ($record instanceof WT_Person) { + if ($record instanceof WT_Individual) { $sex=$record->getSex(); } else { $sex='U'; diff --git a/library/WT/Gedcom/Code/Name.php b/library/WT/Gedcom/Code/Name.php index a5ad95f06f..20470b12ed 100644 --- a/library/WT/Gedcom/Code/Name.php +++ b/library/WT/Gedcom/Code/Name.php @@ -2,7 +2,7 @@ // Functions and logic for GEDCOM "NAME" codes // // webtrees: Web based Family History software -// Copyright (C) 2011 webtrees development team. +// Copyright (C) 2013 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 @@ -31,7 +31,7 @@ class WT_Gedcom_Code_Name { // Translate a code, for an (optional) record public static function getValue($type, $record=null) { - if ($record instanceof WT_Person) { + if ($record instanceof WT_Individual) { $sex=$record->getSex(); } else { $sex='U'; diff --git a/library/WT/Gedcom/Code/Pedi.php b/library/WT/Gedcom/Code/Pedi.php index 9b8a835ce1..783c59b4f1 100644 --- a/library/WT/Gedcom/Code/Pedi.php +++ b/library/WT/Gedcom/Code/Pedi.php @@ -2,7 +2,7 @@ // Functions and logic for GEDCOM "PEDI" codes // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 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 @@ -31,7 +31,7 @@ class WT_Gedcom_Code_Pedi { // Translate a code, for an optional record public static function getValue($type, $record=null) { - if ($record instanceof WT_Person) { + if ($record instanceof WT_Individual) { $sex=$record->getSex(); } else { $sex='U'; diff --git a/library/WT/Gedcom/Code/Rela.php b/library/WT/Gedcom/Code/Rela.php index d4faa8f49c..0f53c835b8 100644 --- a/library/WT/Gedcom/Code/Rela.php +++ b/library/WT/Gedcom/Code/Rela.php @@ -2,7 +2,7 @@ // Functions and logic for GEDCOM "RELA" codes // // webtrees: Web based Family History software -// Copyright (C) 2011 webtrees development team. +// Copyright (C) 2013 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 @@ -39,7 +39,7 @@ class WT_Gedcom_Code_Rela { // Translate a code, for an (optional) record public static function getValue($type, $record=null) { - if ($record instanceof WT_Person) { + if ($record instanceof WT_Individual) { $sex=$record->getSex(); } else { $sex='U'; diff --git a/library/WT/Gedcom/Tag.php b/library/WT/Gedcom/Tag.php index 83a62af317..d79b92b753 100644 --- a/library/WT/Gedcom/Tag.php +++ b/library/WT/Gedcom/Tag.php @@ -2,7 +2,7 @@ // Static GEDCOM data for Tags // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 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 @@ -102,7 +102,7 @@ class WT_Gedcom_Tag { // Translate a tag, for an (optional) record public static function getLabel($tag, $record=null) { - if ($record instanceof WT_Person) { + if ($record instanceof WT_Individual) { $sex=$record->getSex(); } else { $sex='U'; @@ -879,12 +879,6 @@ class WT_Gedcom_Tag { return $facts; } - // Get a list of reference facts that will be displayed in the "Extra information" sidebar module, and at the same time excluded from the personal_facts module - public static function getReferenceFacts() { - return array('CHAN', 'IDNO', 'RFN', 'AFN', 'REFN', 'RIN', '_UID', 'SSN'); - } - - ////////////////////////////////////////////////////////////////////////////// // Definitions for Object, File, Format, Types ////////////////////////////////////////////////////////////////////////////// diff --git a/library/WT/GedcomRecord.php b/library/WT/GedcomRecord.php index 2b67f6caa4..67fbca8730 100644 --- a/library/WT/GedcomRecord.php +++ b/library/WT/GedcomRecord.php @@ -30,305 +30,265 @@ if (!defined('WT_WEBTREES')) { class WT_GedcomRecord { const RECORD_TYPE = 'UNKNOWN'; + const SQL_FETCH = "SELECT o_gedcom FROM `##other` WHERE o_id=? AND o_file=?"; const URL_PREFIX = 'gedrecord.php?pid='; - protected $xref =null; // The record identifier - public $ged_id =null; // The gedcom file, only set if this record comes from the database - protected $_gedrec =null; // Raw gedcom text (unprivatised) - private $gedrec =null; // Raw gedcom text (privatised) - protected $facts =null; - protected $changeEvent=null; - private $disp_public=null; // Can we display details of this record to WT_PRIV_PUBLIC - private $disp_user =null; // Can we display details of this record to WT_PRIV_USER - private $disp_none =null; // Can we display details of this record to WT_PRIV_NONE - private $changed =false; // Is this a new record, pending approval + protected $xref = null; // The record identifier + protected $gedcom_id = null; // The gedcom file + protected $gedcom = null; // GEDCOM data (before any pending edits) + protected $pending = null; // GEDCOM data (after any pending edits) - // Cached results from various functions. - protected $_getAllNames =null; - protected $_getPrimaryName =null; - protected $_getSecondaryName=null; + protected $facts = null; // Array of WT_Fact objects (from $gedcom/$pending) - // Create a GedcomRecord object from either raw GEDCOM data or a database row - public function __construct($data) { - if (is_array($data)) { - // Construct from a row from the database - $this->xref =$data['xref']; - $this->ged_id =$data['ged_id']; - $this->_gedrec=$data['gedrec']; - } else { - // Construct from raw GEDCOM data - $this->_gedrec=$data; - if (preg_match('/^0 (?:@('.WT_REGEX_XREF.')@ )?('.WT_REGEX_TAG.')/', $data, $match)) { - $this->xref=$match[1]; - $this->ged_id=WT_GED_ID; - } - } - } + private $disp_public = null; // Can we display details of this record to WT_PRIV_PUBLIC + private $disp_user = null; // Can we display details of this record to WT_PRIV_USER + private $disp_none = null; // Can we display details of this record to WT_PRIV_NONE - // Get an instance of a GedcomRecord. We either specify - // an XREF (in the current gedcom), or we can provide a row - // from the database (if we anticipate the record hasn't - // been fetched previously). - static public function getInstance($data) { - global $gedcom_record_cache, $GEDCOM; - static $pending_record_cache; + // Cached results from various functions. + protected $_getAllNames = null; + protected $_getPrimaryName = null; + protected $_getSecondaryName = null; + + // Allow getInstance() to return references to existing objects + private static $gedcom_record_cache; + // Fetch all pending edits in one database query + private static $pending_record_cache; - $is_pending=false; // Did this record come from a pending edit + // Create a GedcomRecord object from raw GEDCOM data. + // $gedcom is an empty string for new/pending records + // $pending is null for a record with no pending edits + // $pending is an empty string for records with pending deletions + public function __construct($xref, $gedcom, $pending, $gedcom_id) { + $this->xref = $xref; + $this->gedcom = $gedcom; + $this->pending = $pending; + $this->gedcom_id = $gedcom_id; - if (is_array($data)) { - $ged_id=$data['ged_id']; - $pid =$data['xref']; + // Split the record into facts + if ($gedcom) { + $gedcom_facts = preg_split('/\n(?=1)/s', $gedcom); + array_shift($gedcom_facts); + } else { + $gedcom_facts = array(); + } + if ($pending) { + $pending_facts = preg_split('/\n(?=1)/s', $pending); + array_shift($pending_facts); } else { - $ged_id=get_id_from_gedcom($GEDCOM); - $pid =$data; + $pending_facts = array(); } - // Check the cache first - if (isset($gedcom_record_cache[$pid][$ged_id])) { - return $gedcom_record_cache[$pid][$ged_id]; + $this->facts = array(); + + foreach ($gedcom_facts as $gedcom_fact) { + $fact = new WT_Fact($gedcom_fact, $this, md5($gedcom_fact)); + if ($pending !== null && !in_array($gedcom_fact, $pending_facts)) { + $fact->setIsOld(); + } + $this->facts[] = $fact; + } + foreach ($pending_facts as $pending_fact) { + if (!in_array($pending_fact, $gedcom_facts)) { + $fact = new WT_Fact($pending_fact, $this, md5($pending_fact)); + $fact->setIsNew(); + $this->facts[] = $fact; + } } + } - // Look for the record in the database - if (!is_array($data)) { - $data=static::fetchGedcomRecord($pid, $ged_id); + // Get an instance of a GedcomRecord object. For single records, + // we just receive the XREF. For bulk records (such as lists + // and search results) we can receive the GEDCOM data as well. + static public function getInstance($xref, $gedcom_id=WT_GED_ID, $gedcom=null) { + // Is this record already in the cache? + if (isset(self::$gedcom_record_cache[$xref][$gedcom_id])) { + return self::$gedcom_record_cache[$xref][$gedcom_id]; + } - // If we can edit, then we also need to be able to see pending records. - // Otherwise relationship privacy rules will not allow us to see - // newly added records. - if (WT_USER_CAN_EDIT) { - if (!isset($pending_record_cache[$ged_id])) { - // Fetch all pending records in one database query - $pending_record_cache[$ged_id]=array(); - $rows = WT_DB::prepare( - "SELECT xref, new_gedcom FROM `##change` WHERE status='pending' AND gedcom_id=?" - )->execute(array($ged_id))->fetchAll(); - foreach ($rows as $row) { - $pending_record_cache[$ged_id][$row->xref] = $row->new_gedcom; - } - } + // Do we need to fetch the record from the database? + if ($gedcom === null) { + $gedcom = static::fetchGedcomRecord($xref, $gedcom_id); + } - if (isset($pending_record_cache[$ged_id][$pid])) { - // A pending edit exists for this record - $tmp = $pending_record_cache[$ged_id][$pid]; - // $tmp can be an empty string, indicating the record has - // a pending deletion. Ignore this, as we handle pending - // deletions separately. - if ($tmp) { - $is_pending=true; - $data=$tmp; - } + // If we can edit, then we also need to be able to see pending records. + if (WT_USER_CAN_EDIT) { + if (!isset(self::$pending_record_cache[$gedcom_id])) { + // Fetch all pending records in one database query + self::$pending_record_cache[$gedcom_id]=array(); + $rows = WT_DB::prepare( + "SELECT xref, new_gedcom FROM `##change` WHERE status='pending' AND gedcom_id=?" + )->execute(array($gedcom_id))->fetchAll(); + foreach ($rows as $row) { + self::$pending_record_cache[$gedcom_id][$row->xref] = $row->new_gedcom; } } - // If we still didn't find it, it doesn't exist - if (!$data) { - return null; + if (isset(self::$pending_record_cache[$gedcom_id][$xref])) { + // A pending edit exists for this record + $pending = self::$pending_record_cache[$gedcom_id][$xref]; + } else { + $pending = null; } + } else { + // There are no pending changes for this record + $pending = null; + } + + // No such record exists + if ($gedcom === null && $pending === null) { + return null; } // Create the object - if (is_array($data)) { - $type=$data['type']; - } elseif (preg_match('/^0 @'.WT_REGEX_XREF.'@ ('.WT_REGEX_TAG.')/', $data, $match)) { - $type=$match[1]; + if (preg_match('/^0 @' . WT_REGEX_XREF . '@ (' . WT_REGEX_TAG . ')/', $gedcom.$pending, $match)) { + $type = $match[1]; } else { - $type=''; + throw new Exception('Unrecognised GEDCOM record: ' . $gedcom); } + switch($type) { case 'INDI': - $object=new WT_Person($data); + $record = new WT_Individual($xref, $gedcom, $pending, $gedcom_id); break; case 'FAM': - $object=new WT_Family($data); + $record = new WT_Family($xref, $gedcom, $pending, $gedcom_id); break; case 'SOUR': - $object=new WT_Source($data); + $record = new WT_Source($xref, $gedcom, $pending, $gedcom_id); break; case 'OBJE': - $object=new WT_Media($data); + $record = new WT_Media($xref, $gedcom, $pending, $gedcom_id); break; case 'REPO': - $object=new WT_Repository($data); + $record = new WT_Repository($xref, $gedcom, $pending, $gedcom_id); break; case 'NOTE': - $object=new WT_Note($data); + $record = new WT_Note($xref, $gedcom, $pending, $gedcom_id); break; default: - $object=new WT_GedcomRecord($data); - break; - } - - // This is an object from the database, so indicate which gedcom it comes from. - $object->ged_id=$ged_id; - - if ($is_pending) { - $object->setChanged(true); + throw new Exception('No support for GEDCOM record type: ' . $type); } // Store it in the cache - $gedcom_record_cache[$object->xref][$object->ged_id]=&$object; - return $object; + self::$gedcom_record_cache[$xref][$gedcom_id] = $record; + + return $record; } - private static function fetchGedcomRecord($xref, $ged_id) { + private static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; // We don't know what type of object this is. Try each one in turn. - $row=WT_Person::fetchGedcomRecord($xref, $ged_id); - if ($row) { - return $row; + $data = WT_Individual::fetchGedcomRecord($xref, $gedcom_id); + if ($data) { + return $data; } - $row=WT_Family::fetchGedcomRecord($xref, $ged_id); - if ($row) { - return $row; + $data = WT_Family::fetchGedcomRecord($xref, $gedcom_id); + if ($data) { + return $data; } - $row=WT_Source::fetchGedcomRecord($xref, $ged_id); - if ($row) { - return $row; + $data = WT_Source::fetchGedcomRecord($xref, $gedcom_id); + if ($data) { + return $data; } - $row=WT_Repository::fetchGedcomRecord($xref, $ged_id); - if ($row) { - return $row; + $data = WT_Repository::fetchGedcomRecord($xref, $gedcom_id); + if ($data) { + return $data; } - $row=WT_Media::fetchGedcomRecord($xref, $ged_id); - if ($row) { - return $row; + $data = WT_Media::fetchGedcomRecord($xref, $gedcom_id); + if ($data) { + return $data; } - $row=WT_Note::fetchGedcomRecord($xref, $ged_id); - if ($row) { - return $row; + $data = WT_Note::fetchGedcomRecord($xref, $gedcom_id); + if ($data) { + return $data; } // Some other type of record... if (is_null($statement)) { - $statement=WT_DB::prepare( - "SELECT o_type AS type, o_id AS xref, o_file AS ged_id, o_gedcom AS gedrec ". - "FROM `##other` WHERE o_id=? AND o_file=? AND o_type NOT IN ('REPO', 'NOTE')" - ); + $statement=WT_DB::prepare("SELECT o_gedcom FROM `##other` WHERE o_id=? AND o_file=?"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } - /** - * get the xref - * @return string returns the person ID - */ + // XREF public function getXref() { return $this->xref; } - /** - * get the gedcom file - * @return string returns the person ID - */ - public function getGedId() { - return $this->ged_id; + + // GEDCOM ID + public function getGedcomId() { + return $this->gedcom_id; } - /** - * get gedcom record - */ - public function getGedcomRecord() { - if ($this->gedrec===null) { - list($this->gedrec)=$this->privatizeGedcom(WT_USER_ACCESS_LEVEL); + // Application code should access data via WT_Fact objects + public function getGedcom() { + if ($this->pending === null) { + return $this->gedcom; + } else { + return $this->pending; } - return $this->gedrec; - } - /** - * set gedcom record - */ - public function setGedcomRecord($gcRec) { - $this->gedrec = $gcRec; } - /** - * set if this is a changed record from the gedcom file - * @param boolean $changed - */ - public function setChanged($changed) { - $this->changed = $changed; + + // Does this record have a pending change? + public function isNew() { + return $this->pending !== null; } - /** - * get if this is a changed record from the gedcom file - * @return boolean - */ - public function getChanged() { - return $this->changed; + + // Does this record have a pending deletion? + public function isOld() { + return $this->pending === ''; } - /** - * check if this object is equal to the given object - * @param GedcomRecord $obj - */ + // Are two records the same? public function equals($obj) { - return !is_null($obj) && $this->xref==$obj->getXref(); + return $obj && $this->xref==$obj->getXref(); } - // Generate a URL to this record, suitable for use in HTML + // Generate a URL to this record, suitable for use in HTML, etc. public function getHtmlUrl() { - return self::_getLinkUrl(static::URL_PREFIX, '&'); + return $this->_getLinkUrl(static::URL_PREFIX, '&'); } // Generate a URL to this record, suitable for use in javascript, HTTP headers, etc. public function getRawUrl() { - return self::_getLinkUrl(static::URL_PREFIX, '&'); + return $this->_getLinkUrl(static::URL_PREFIX, '&'); } - private function _getLinkUrl($link, $separator) { - if ($this->ged_id) { - // If the record was created from the database, we know the gedcom - return $link.$this->getXref().$separator.'ged='.rawurlencode(get_gedcom_from_id($this->ged_id)); - } else { - // If the record was created from a text string, assume the current gedcom - return $link.$this->getXref().$separator.'ged='.WT_GEDURL; - } - } - - /** - * return an absolute url for linking to this record from another site - * - */ + // Generate an absolute URL for this record, suitable for sitemap.xml, RSS feeds, etc. public function getAbsoluteLinkUrl() { - return WT_SERVER_NAME.WT_SCRIPT_PATH.$this->getHtmlUrl(); + return WT_SERVER_NAME . WT_SCRIPT_PATH . $this->getHtmlUrl(); } - /** - * check if this record has been marked for deletion - * @return boolean - */ - public function isMarkedDeleted() { - $tmp=WT_DB::prepare( - "SELECT new_gedcom". - " FROM `##change`". - " WHERE status='pending' AND gedcom_id=? AND xref=?". - " ORDER BY change_id desc". - " LIMIT 1" - )->execute(array($this->ged_id, $this->xref))->fetchOne(); - - return $tmp===''; + private function _getLinkUrl($link, $separator) { + if ($this->gedcom_id == WT_GED_ID) { + return $link . $this->getXref() . $separator . 'ged=' . WT_GEDURL; + } else { + return $link . $this->getXref() . $separator . 'ged=' . rawurlencode(get_gedcom_from_id($this->gedcom_id)); + } } // Work out whether this record can be shown to a user with a given access level - private function _canDisplayDetails($access_level) { - global $person_privacy, $HIDE_LIVE_PEOPLE, $PRIVACY_CHECKS; + private function _canShow($access_level) { + global $person_privacy, $HIDE_LIVE_PEOPLE; - //-- keep a count of how many times we have checked for privacy - ++$PRIVACY_CHECKS; // This setting would better be called "$ENABLE_PRIVACY" if (!$HIDE_LIVE_PEOPLE) { return true; } // We should always be able to see our own record (unless an admin is applying download restrictions) - if ($this->getXref()==WT_USER_GEDCOM_ID && $this->getGedId()==WT_GED_ID && $access_level==WT_USER_ACCESS_LEVEL) { + if ($this->getXref()==WT_USER_GEDCOM_ID && $this->getGedcomId()==WT_GED_ID && $access_level==WT_USER_ACCESS_LEVEL) { return true; } // Does this record have a RESN? - if (strpos($this->_gedrec, "\n1 RESN confidential")) { + if (strpos($this->gedcom, "\n1 RESN confidential")) { return WT_PRIV_NONE>=$access_level; } - if (strpos($this->_gedrec, "\n1 RESN privacy")) { + if (strpos($this->gedcom, "\n1 RESN privacy")) { return WT_PRIV_USER>=$access_level; } - if (strpos($this->_gedrec, "\n1 RESN none")) { + if (strpos($this->gedcom, "\n1 RESN none")) { return true; } @@ -343,11 +303,11 @@ class WT_GedcomRecord { } // Different types of record have different privacy rules - return $this->_canDisplayDetailsByType($access_level); + return $this->_canShowByType($access_level); } // Each object type may have its own special rules, and re-implement this function. - protected function _canDisplayDetailsByType($access_level) { + protected function _canShowByType($access_level) { global $global_facts; if (isset($global_facts[static::RECORD_TYPE])) { @@ -360,23 +320,23 @@ class WT_GedcomRecord { } // Can the details of this record be shown? - public function canDisplayDetails($access_level=WT_USER_ACCESS_LEVEL) { + public function canShow($access_level=WT_USER_ACCESS_LEVEL) { // CACHING: this function can take three different parameters, // and therefore needs three different caches for the result. switch ($access_level) { case WT_PRIV_PUBLIC: // visitor if ($this->disp_public===null) { - $this->disp_public=$this->_canDisplayDetails(WT_PRIV_PUBLIC); + $this->disp_public=$this->_canShow(WT_PRIV_PUBLIC); } return $this->disp_public; case WT_PRIV_USER: // member if ($this->disp_user===null) { - $this->disp_user=$this->_canDisplayDetails(WT_PRIV_USER); + $this->disp_user=$this->_canShow(WT_PRIV_USER); } return $this->disp_user; case WT_PRIV_NONE: // admin if ($this->disp_none===null) { - $this->disp_none=$this->_canDisplayDetails(WT_PRIV_NONE); + $this->disp_none=$this->_canShow(WT_PRIV_NONE); } return $this->disp_none; case WT_PRIV_HIDE: // hidden from admins @@ -390,15 +350,13 @@ class WT_GedcomRecord { } // Can the name of this record be shown? - public function canDisplayName($access_level=WT_USER_ACCESS_LEVEL) { - return $this->canDisplayDetails($access_level); + public function canShowName($access_level=WT_USER_ACCESS_LEVEL) { + return $this->canShow($access_level); } // Can we edit this record? - // We use the unprivatized _gedrec as we must take account - // of the RESN tag, even if we are not permitted to see it. public function canEdit() { - return WT_USER_GEDCOM_ADMIN || WT_USER_CAN_EDIT && strpos($this->_gedrec, "\n1 RESN locked")===false; + return WT_USER_GEDCOM_ADMIN || WT_USER_CAN_EDIT && strpos($this->gedcom, "\n1 RESN locked")===false; } // Remove private data from the raw gedcom record. @@ -408,28 +366,25 @@ class WT_GedcomRecord { if ($access_level==WT_PRIV_HIDE) { // We may need the original record, for example when downloading a GEDCOM or clippings cart - return array($this->_gedrec, ''); - } elseif ($this->canDisplayDetails($access_level)) { + return array($this->gedcom, ''); + } elseif ($this->canShow($access_level)) { // The record is not private, but the individual facts may be. // Include the entire first line (for NOTE records) - list($gedrec)=explode("\n", $this->_gedrec, 2); + list($gedrec)=explode("\n", $this->gedcom, 2); - $private_gedrec=''; // Check each of the sub facts for access - preg_match_all('/\n1 .*(?:\n[2-9].*)*/', $this->_gedrec, $matches); + preg_match_all('/\n1 .*(?:\n[2-9].*)*/', $this->gedcom, $matches); foreach ($matches[0] as $match) { - if (canDisplayFact($this->xref, $this->ged_id, $match, $access_level)) { + if (canDisplayFact($this->xref, $this->gedcom_id, $match, $access_level)) { $gedrec.=$match; - } else { - $private_gedrec.=$match; } } - return array($gedrec, $private_gedrec); + return $gedrec; } else { // We cannot display the details, but we may be able to display // limited data, such as links to other records. - return array($this->createPrivateGedcomRecord($access_level), ''); + return $this->createPrivateGedcomRecord($access_level); } } @@ -440,10 +395,10 @@ class WT_GedcomRecord { // Convert a name record into sortable and full/display versions. This default // should be OK for simple record types. INDI/FAM records will need to redefine it. - protected function _addName($type, $value, $gedrec) { + protected function _addName($type, $value, $gedcom) { $this->_getAllNames[]=array( 'type'=>$type, - 'sort'=>preg_replace('/([0-9]+)/e', 'substr("000000000\\1", -10)', $value), + 'sort'=>preg_replace_callback('/([0-9]+)/', function($matches) { return str_pad($matches[0], 10, '0', STR_PAD_LEFT); }, $value), 'full'=>'<span dir="auto">'.htmlspecialchars($value).'</span>', // This is used for display 'fullNN'=>$value, // This goes into the database ); @@ -462,10 +417,10 @@ class WT_GedcomRecord { if (is_null($this->_getAllNames)) { $this->_getAllNames=array(); - if ($this->canDisplayName()) { + if ($this->canShowName()) { $sublevel=$level+1; $subsublevel=$sublevel+1; - if (preg_match_all("/^{$level} ({$fact}) (.+)((\n[{$sublevel}-9].+)*)/m", $this->getGedcomRecord(), $matches, PREG_SET_ORDER)) { + if (preg_match_all("/^{$level} ({$fact}) (.+)((\n[{$sublevel}-9].+)*)/m", $this->getGedcom(), $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { // Treat 1 NAME / 2 TYPE married the same as _MARNM if ($match[1]=='NAME' && strpos($match[3], "\n2 TYPE married")!==false) { @@ -581,20 +536,20 @@ class WT_GedcomRecord { // Allow native PHP functions such as array_intersect() to work with objects public function __toString() { - return $this->xref.'@'.$this->ged_id; + return $this->xref.'@'.$this->gedcom_id; } // Static helper function to sort an array of objects by name // Records whose names cannot be displayed are sorted at the end. static function Compare($x, $y) { - if ($x->canDisplayName()) { - if ($y->canDisplayName()) { + if ($x->canShowName()) { + if ($y->canShowName()) { return utf8_strcasecmp($x->getSortName(), $y->getSortName()); } else { return -1; // only $y is private } } else { - if ($y->canDisplayName()) { + if ($y->canShowName()) { return 1; // only $x is private } else { return 0; // both $x and $y private @@ -611,8 +566,8 @@ class WT_GedcomRecord { return $tmp; } else { if ( - preg_match('/^\d\d:\d\d:\d\d/', get_gedcom_value('DATE:TIME', 2, $chan_x->getGedcomRecord()).':00', $match_x) && - preg_match('/^\d\d:\d\d:\d\d/', get_gedcom_value('DATE:TIME', 2, $chan_y->getGedcomRecord()).':00', $match_y) + preg_match('/^\d\d:\d\d:\d\d/', get_gedcom_value('DATE:TIME', 2, $chan_x->getGedcom()).':00', $match_x) && + preg_match('/^\d\d:\d\d:\d\d/', get_gedcom_value('DATE:TIME', 2, $chan_y->getGedcom()).':00', $match_y) ) { return strcmp($match_x[0], $match_y[0]); } else { @@ -623,22 +578,22 @@ class WT_GedcomRecord { // Get variants of the name public function getFullName() { - if ($this->canDisplayName()) { - $tmp=$this->getAllNames(); + if ($this->canShowName()) { + $tmp = $this->getAllNames(); return $tmp[$this->getPrimaryName()]['full']; } else { return WT_I18N::translate('Private'); } } public function getSortName() { - // The sortable name is never displayed, no need to call canDisplayName() - $tmp=$this->getAllNames(); + // The sortable name is never displayed, no need to call canShowName() + $tmp = $this->getAllNames(); return $tmp[$this->getPrimaryName()]['sort']; } // Get the fullname in an alternative character set public function getAddName() { - if ($this->canDisplayName() && $this->getPrimaryName()!=$this->getSecondaryName()) { - $all_names=$this->getAllNames(); + if ($this->canShowName() && $this->getPrimaryName()!=$this->getSecondaryName()) { + $all_names = $this->getAllNames(); return $all_names[$this->getSecondaryName()]['full']; } else { return null; @@ -672,7 +627,7 @@ class WT_GedcomRecord { // Extract/format the first fact from a list of facts. public function format_first_major_fact($facts, $style) { - foreach ($this->getAllFactsByType(explode('|', $facts)) as $event) { + foreach ($this->getFacts($facts) as $event) { // Only display if it has a date or place (or both) if (($event->getDate()->isOK() || $event->getPlace()) && $event->canShow()) { switch ($style) { @@ -688,22 +643,22 @@ class WT_GedcomRecord { // Fetch the records that link to this one public function fetchLinkedIndividuals() { - return fetch_linked_indi($this->getXref(), static::RECORD_TYPE, $this->ged_id); + return fetch_linked_indi($this->getXref(), static::RECORD_TYPE, $this->gedcom_id); } public function fetchLinkedFamilies() { - return fetch_linked_fam($this->getXref(), static::RECORD_TYPE, $this->ged_id); + return fetch_linked_fam($this->getXref(), static::RECORD_TYPE, $this->gedcom_id); } public function fetchLinkedNotes() { - return fetch_linked_note($this->getXref(), static::RECORD_TYPE, $this->ged_id); + return fetch_linked_note($this->getXref(), static::RECORD_TYPE, $this->gedcom_id); } public function fetchLinkedSources() { - return fetch_linked_sour($this->getXref(), static::RECORD_TYPE, $this->ged_id); + return fetch_linked_sour($this->getXref(), static::RECORD_TYPE, $this->gedcom_id); } public function fetchLinkedRepositories() { - return fetch_linked_repo($this->getXref(), static::RECORD_TYPE, $this->ged_id); + return fetch_linked_repo($this->getXref(), static::RECORD_TYPE, $this->gedcom_id); } public function fetchLinkedMedia() { - return fetch_linked_obje($this->getXref(), static::RECORD_TYPE, $this->ged_id); + return fetch_linked_obje($this->getXref(), static::RECORD_TYPE, $this->gedcom_id); } // Get all attributes (e.g. DATE or PLAC) from an event (e.g. BIRT or MARR). @@ -712,154 +667,48 @@ class WT_GedcomRecord { // calendars, place-names in both latin and hebrew character sets, etc. // It also allows us to combine dates/places from different events in the summaries. public function getAllEventDates($event) { - $dates=array(); - foreach ($this->getAllFactsByType($event) as $event) { + $dates = array(); + foreach ($this->getFacts($event) as $event) { if ($event->getDate()->isOK()) { - $dates[]=$event->getDate(); + $dates[] = $event->getDate(); } } return $dates; } public function getAllEventPlaces($event) { - $places=array(); - foreach ($this->getAllFactsByType($event) as $event) { - if (preg_match_all('/\n(?:2 PLAC|3 (?:ROMN|FONE|_HEB)) +(.+)/', $event->getGedcomRecord(), $ged_places)) { + $places = array(); + foreach ($this->getFacts($event) as $event) { + if (preg_match_all('/\n(?:2 PLAC|3 (?:ROMN|FONE|_HEB)) +(.+)/', $event->getGedcom(), $ged_places)) { foreach ($ged_places[1] as $ged_place) { - $places[]=$ged_place; + $places[] = $ged_place; } } } return $places; } - // Get the first WT_Event for the given fact type + // Get the first WT_Fact for the given fact type public function getFactByType($tag) { foreach ($this->getFacts() as $fact) { - if ($fact->getTag()==$tag) { + if ($fact->getTag() == $tag) { return $fact; } } return null; } - // Return an array of events that match the given types - public function getAllFactsByType($tags) { - if (is_string($tags)) { - $tags = array($tags); - } - $facts = array(); - foreach ($tags as $tag) { - foreach ($this->getFacts() as $fact) { - if ($fact->getTag()==$tag) { - $facts[]=$fact; - } - } - } - return $facts; - } - - /** - * returns an array of all of the facts - * @return Array - */ - public function getFacts() { - $this->parseFacts(); - return $this->facts; - } - - /** - * Get the CHAN event for this record - * - * @return WT_Event - */ - public function getChangeEvent() { - if (is_null($this->changeEvent)) { - $this->changeEvent = $this->getFactByType('CHAN'); - } - return $this->changeEvent; - } - - /** - * Parse the facts from the record - */ - public function parseFacts() { - //-- only run this function once - if (!is_null($this->facts) && is_array($this->facts)) { - return; - } - $this->facts=array(); - //-- don't run this function if privacy does not allow viewing of details - if (!$this->canDisplayDetails()) { - return; - } - //-- find all the fact information - $indilines = explode("\n", $this->getGedcomRecord()); // -- find the number of lines in the individuals record - $lct = count($indilines); - $factrec = ''; // -- complete fact record - $line = ''; // -- temporary line buffer - $linenum=1; - for ($i=1; $i<=$lct; $i++) { - if ($i<$lct) { - $line = $indilines[$i]; - } else { - $line=' '; - } - if (empty($line)) { - $line=' '; - } - if ($i==$lct||$line{0}==1) { - if ($i>1) { - $this->facts[] = new WT_Event($factrec, $this, $linenum); - } - $factrec = $line; - $linenum = $i; - } - else $factrec .= "\n".$line; - } - } - - /** - * Merge the facts from another GedcomRecord object into this object - * for generating a diff view - * @param GedcomRecord $diff the record to compare facts with - */ - public function diffMerge($diff) { - if (is_null($diff)) { - return; - } - $this->parseFacts(); - $diff->parseFacts(); - - //-- update old facts - foreach ($this->facts as $key=>$event) { - $found = false; - foreach ($diff->facts as $indexval => $newevent) { - $newfact = $newevent->getGedcomRecord(); - $newfact=preg_replace("/\\\/", '/', $newfact); - if (trim($newfact)==trim($event->getGedcomRecord())) { - $found = true; - break; - } - } - if (!$found) { - $this->facts[$key]->setIsOld(); - } - } - //-- look for new facts - foreach ($diff->facts as $key=>$newevent) { - $found = false; - foreach ($this->facts as $indexval => $event) { - $newfact = $newevent->getGedcomRecord(); - $newfact=preg_replace("/\\\/", '/', $newfact); - if (trim($newfact)==trim($event->getGedcomRecord())) { - $found = true; - break; + // The facts and events for this record + public function getFacts($filter=null) { + if ($filter === null) { + return $this->facts; + } else { + $facts=array(); + foreach ($this->facts as $fact) { + if (preg_match('/^' . $filter . '$/', $fact->getTag())) { + $facts[] = $fact; } } - if (!$found) { - $newevent->setIsNew(); - $this->facts[]=$newevent; - } + return $facts; } } @@ -871,26 +720,18 @@ class WT_GedcomRecord { $srec = $srec[0]; return get_gedcom_value('DATE', 2, $srec); } - public function getEventSource($event) { - $srec = $this->getAllEvents($event); - if (!$srec) { - return ''; - } - $srec = $srec[0]; - return get_sub_record('SOUR', 2, $srec); - } ////////////////////////////////////////////////////////////////////////////// // Get the last-change timestamp for this record, either as a formatted string // (for display) or as a unix timestamp (for sorting) ////////////////////////////////////////////////////////////////////////////// public function LastChangeTimestamp($sorting=false) { - $chan = $this->getChangeEvent(); + $chan = $this->getFactByType('CHAN'); if ($chan) { // The record does have a CHAN event $d = $chan->getDate()->MinDate(); - if (preg_match('/^(\d\d):(\d\d):(\d\d)/', get_gedcom_value('DATE:TIME', 2, $chan->getGedcomRecord()).':00', $match)) { + if (preg_match('/^(\d\d):(\d\d):(\d\d)/', get_gedcom_value('DATE:TIME', 2, $chan->getGedcom()).':00', $match)) { $t=mktime((int)$match[1], (int)$match[2], (int)$match[3], (int)$d->Format('%n'), (int)$d->Format('%j'), (int)$d->Format('%Y')); } else { $t=mktime(0, 0, 0, (int)$d->Format('%n'), (int)$d->Format('%j'), (int)$d->Format('%Y')); @@ -914,16 +755,192 @@ class WT_GedcomRecord { // Get the last-change user for this record ////////////////////////////////////////////////////////////////////////////// public function LastChangeUser() { - $chan = $this->getChangeEvent(); + $chan = $this->getFactByType('CHAN'); if (is_null($chan)) { return ' '; } - $chan_user = $chan->getValue('_WT_USER'); + $chan_user = $chan->getAttribute('_WT_USER'); if (empty($chan_user)) { return ' '; } return $chan_user; } + + ////////////////////////////////////////////////////////////////////////////// + // CRUD operations + ////////////////////////////////////////////////////////////////////////////// + + // Replace a (possibly empty) fact with a new (possibly empty) fact. + // This allows create/update/delete of individual facts + public function updateFact($fact_id, $gedcom, $update_chan) { + if (strpos("\r", $gedcom)!==false) { + // MSDOS line endings will break things in horrible ways + throw new Exception('Evil line endings found in WT_GedcomRecord::updateRecord(' . $gedcom . ')'); + } + if ($this->pending==='') { + throw new Exception('Cannot edit a deleted record'); + } + if ($gedcom && !preg_match('/^1 ' . WT_REGEX_TAG . '/', $gedcom)) { + throw new Exception('Invalid GEDCOM data passed to WT_GedcomRecord::updateFact()'); + } + + if ($this->pending) { + $old_gedcom = $this->pending; + } else { + $old_gedcom = $this->gedcom; + } + + $new_gedcom = '0 @' . $this->getXref() . '@ ' . static::RECORD_TYPE; + $old_chan = $this->getFactByType('CHAN'); + // Replacing (or deleting) an existing fact + foreach ($this->getFacts() as $fact) { + if ($fact->getFactId() == $fact_id) { + if ($gedcom) { + $new_gedcom .= "\n" . $gedcom; + } + } elseif ($fact->getTag()!='CHAN' || !$update_chan) { + $new_gedcom .= "\n" . $fact->getGedcom(); + } + } + if ($update_chan) { + $new_gedcom .= "\n1 CHAN\n2 DATE " . date('d M Y') . "\n3 TIME " . date('H:i:s') . "\n2 _WT_USER " . WT_USER_NAME; + } + + // Adding a new fact + if (!$fact_id) { + $new_gedcom .= "\n" . $gedcom; + } + + if ($new_gedcom != $old_gedcom) { + // Save the changes + WT_DB::prepare( + "INSERT INTO `##change` (gedcom_id, xref, old_gedcom, new_gedcom, user_id) VALUES (?, ?, ?, ?, ?)" + )->execute(array( + $this->gedcom_id, + $this->xref, + $old_gedcom, + $new_gedcom, + WT_USER_ID + )); + + // Clear the cache + self::$gedcom_record_cache = null; + self::$pending_record_cache = null; + + if (get_user_setting(WT_USER_ID, 'auto_accept')) { + accept_all_changes($xref, $ged_id); + } + } + } + + static public function createRecord($gedcom, $gedcom_id) { + if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedcom, $match)) { + $xref = $match[1]; + $type = $match[2]; + } else { + throw new Exception('Invalid argument to WT_GedcomRecord::createRecord(' . $gedcom . ')'); + } + if (strpos("\r", $gedcom)!==false) { + // MSDOS line endings will break things in horrible ways + throw new Exception('Evil line endings found in WT_GedcomRecord::createRecord(' . $gedcom . ')'); + } + + // webtrees creates XREFs containing digits. Anything else (e.g. “new”) is just a placeholder. + if (!preg_match('/\d/', $xref)) { + $xref = get_new_xref($type); + $gedcom = preg_replace('/^0 @(' . WT_REGEX_XREF . ')@/', '0 @' . $xref . '@', $gedcom); + } + + // Create a change record, if not already present + if (!preg_match('/\n1 CHAN/', $gedcom)) { + $gedcom .= "\n1 CHAN\n2 DATE " . date('d M Y') . "\n3 TIME " . date('H:i:s') . "\n2 _WT_USER " . WT_USER_NAME; + } + + // Create a pending change + WT_DB::prepare( + "INSERT INTO `##change` (gedcom_id, xref, old_gedcom, new_gedcom, user_id) VALUES (?, ?, '', ?, ?)" + )->execute(array( + $gedcom_id, + $xref, + $gedcom, + $gedcom_id + )); + + // Accept this pending change + if (get_user_setting(WT_USER_ID, 'auto_accept')) { + accept_all_changes($this->xref, $this->gedcom_id); + } + + // Clear this record from the cache + self::$pending_record_cache = null; + + AddToLog('Create: ' . $type . ' ' . $xref, 'edit'); + + // Return the newly created record + return WT_GedcomRecord::getInstance($xref); + } + + private static function readRecord($xref, $gedcom_id) { + return WT_DB::prepare(static::SQL_FETCH)->execute(array($xref, $gedcom_id))->fetchOne(); + } + + public function updateRecord($gedcom, $update_chan) { + if (strpos("\r", $gedcom)!==false) { + // MSDOS line endings will break things in horrible ways + throw new Exception('Evil line endings found in WT_GedcomRecord::updateRecord(' . $gedcom . ')'); + } + + // Update the CHAN record + if ($update_chan) { + $gedcom = preg_replace('/\n1 CHAN(\n[2-9].*)*/', '', $gedcom); + $gedcom .= "\n1 CHAN\n2 DATE " . date('d M Y') . "\n3 TIME " . date('H:i:s') . "\n2 _WT_USER " . WT_USER_NAME; + } + + // Create a pending change + WT_DB::prepare( + "INSERT INTO `##change` (gedcom_id, xref, old_gedcom, new_gedcom, user_id) VALUES (?, ?, ?, ?, ?)" + )->execute(array( + $this->gedcom_id, + $this->xref, + $this->getGedcom(), + $gedcom, + WT_USER_ID + )); + + // Accept this pending change + if (get_user_setting(WT_USER_ID, 'auto_accept')) { + accept_all_changes($this->xref, $this->gedcom_id); + } + + // Clear the cache + self::$gedcom_record_cache = null; + self::$pending_record_cache = null; + + AddToLog('Update: ' . self::RECORD_TYPE . ' ' . $this->xref, 'edit'); + } + + public function deleteRecord() { + // Create a pending change + WT_DB::prepare( + "INSERT INTO `##change` (gedcom_id, xref, old_gedcom, new_gedcom, user_id) VALUES (?, ?, ?, '', ?)" + )->execute(array( + $this->gedcom_id, + $this->xref, + $this->getGedcom(), + WT_USER_ID + )); + + // Accept this pending change + if (get_user_setting(WT_USER_ID, 'auto_accept')) { + accept_all_changes($this->xref, $this->gedcom_id); + } + + // Clear the cache + self::$gedcom_record_cache = null; + self::$pending_record_cache = null; + + AddToLog('Delete: ' . self::RECORD_TYPE . ' ' . $this->xref, 'edit'); + } } diff --git a/library/WT/Person.php b/library/WT/Individual.php index a2ab1229af..babefa9fbe 100644 --- a/library/WT/Person.php +++ b/library/WT/Individual.php @@ -1,5 +1,5 @@ <?php -// Class file for a person +// Class file for an individual // // webtrees: Web based Family History software // Copyright (C) 2013 webtrees development team. @@ -28,15 +28,11 @@ if (!defined('WT_WEBTREES')) { exit; } -class WT_Person extends WT_GedcomRecord { +class WT_Individual extends WT_GedcomRecord { const RECORD_TYPE = 'INDI'; + const SQL_FETCH = "SELECT i_gedcom FROM `##individuals` WHERE i_id=? AND i_file=?"; const URL_PREFIX = 'individual.php?pid='; - var $indifacts = array(); - var $otherfacts = array(); - var $globalfacts = array(); - var $mediafacts = array(); - var $facts_parsed = false; var $label = ''; var $highlightedimage = null; var $file = ''; @@ -59,21 +55,21 @@ class WT_Person extends WT_GedcomRecord { private $_getEstimatedDeathDate=null; // Can the name of this record be shown? - public function canDisplayName($access_level=WT_USER_ACCESS_LEVEL) { + public function canShowName($access_level=WT_USER_ACCESS_LEVEL) { global $SHOW_LIVING_NAMES; - return $SHOW_LIVING_NAMES>=$access_level || $this->canDisplayDetails($access_level); + return $SHOW_LIVING_NAMES>=$access_level || $this->canShow($access_level); } // Implement person-specific privacy logic - protected function _canDisplayDetailsByType($access_level) { + protected function _canShowByType($access_level) { global $SHOW_DEAD_PEOPLE, $KEEP_ALIVE_YEARS_BIRTH, $KEEP_ALIVE_YEARS_DEATH; // Dead people... if ($SHOW_DEAD_PEOPLE>=$access_level && $this->isDead()) { $keep_alive=false; if ($KEEP_ALIVE_YEARS_BIRTH) { - preg_match_all('/\n1 (?:'.WT_EVENTS_BIRT.').*(?:\n[2-9].*)*(?:\n2 DATE (.+))/', $this->_gedrec, $matches, PREG_SET_ORDER); + preg_match_all('/\n1 (?:'.WT_EVENTS_BIRT.').*(?:\n[2-9].*)*(?:\n2 DATE (.+))/', $this->gedcom, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $date=new WT_Date($match[1]); if ($date->isOK() && $date->gregorianYear()+$KEEP_ALIVE_YEARS_BIRTH > date('Y')) { @@ -83,7 +79,7 @@ class WT_Person extends WT_GedcomRecord { } } if ($KEEP_ALIVE_YEARS_DEATH) { - preg_match_all('/\n1 (?:'.WT_EVENTS_DEAT.').*(?:\n[2-9].*)*(?:\n2 DATE (.+))/', $this->_gedrec, $matches, PREG_SET_ORDER); + preg_match_all('/\n1 (?:'.WT_EVENTS_DEAT.').*(?:\n[2-9].*)*(?:\n2 DATE (.+))/', $this->gedcom, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $date=new WT_Date($match[1]); if ($date->isOK() && $date->gregorianYear()+$KEEP_ALIVE_YEARS_DEATH > date('Y')) { @@ -97,8 +93,8 @@ class WT_Person extends WT_GedcomRecord { } } // Consider relationship privacy (unless an admin is applying download restrictions) - if (WT_USER_GEDCOM_ID && WT_USER_PATH_LENGTH && $this->getGedId()==WT_GED_ID && $access_level=WT_USER_ACCESS_LEVEL) { - $self = WT_Person::getInstance(WT_USER_GEDCOM_ID); + if (WT_USER_GEDCOM_ID && WT_USER_PATH_LENGTH && $this->getGedcomId()==WT_GED_ID && $access_level=WT_USER_ACCESS_LEVEL) { + $self = WT_Individual::getInstance(WT_USER_GEDCOM_ID); if ($self) { return get_relationship($this, $self, true, WT_USER_PATH_LENGTH)!==false; } @@ -114,9 +110,9 @@ class WT_Person extends WT_GedcomRecord { $rec='0 @'.$this->xref.'@ INDI'; if ($SHOW_LIVING_NAMES>=$access_level) { // Show all the NAME tags, including subtags - preg_match_all('/\n1 NAME.*(?:\n[2-9].*)*/', $this->_gedrec, $matches); + preg_match_all('/\n1 NAME.*(?:\n[2-9].*)*/', $this->gedcom, $matches); foreach ($matches[0] as $match) { - if (canDisplayFact($this->xref, $this->ged_id, $match, $access_level)) { + if (canDisplayFact($this->xref, $this->gedcom_id, $match, $access_level)) { $rec.=$match; } } @@ -124,31 +120,29 @@ class WT_Person extends WT_GedcomRecord { $rec.="\n1 NAME ".WT_I18N::translate('Private'); } // Just show the 1 FAMC/FAMS tag, not any subtags, which may contain private data - preg_match_all('/\n1 (?:FAMC|FAMS) @('.WT_REGEX_XREF.')@/', $this->_gedrec, $matches, PREG_SET_ORDER); + preg_match_all('/\n1 (?:FAMC|FAMS) @('.WT_REGEX_XREF.')@/', $this->gedcom, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $rela=WT_Family::getInstance($match[1]); - if ($rela && ($SHOW_PRIVATE_RELATIONSHIPS || $rela->canDisplayDetails($access_level))) { + if ($rela && ($SHOW_PRIVATE_RELATIONSHIPS || $rela->canShow($access_level))) { $rec.=$match[0]; } } // Don't privatize sex. - if (preg_match('/\n1 SEX [MFU]/', $this->_gedrec, $match)) { + if (preg_match('/\n1 SEX [MFU]/', $this->gedcom, $match)) { $rec.=$match[0]; } return $rec; } // Fetch the record from the database - protected static function fetchGedcomRecord($xref, $ged_id) { + protected static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; if ($statement===null) { - $statement=WT_DB::prepare( - "SELECT 'INDI' AS type, i_id AS xref, i_file AS ged_id, i_gedcom AS gedrec ". - "FROM `##individuals` WHERE i_id=? AND i_file=?" - ); + $statement=WT_DB::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_id=? AND i_file=?"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } // Static helper function to sort an array of people by birth date @@ -167,12 +161,12 @@ class WT_Person extends WT_GedcomRecord { global $MAX_ALIVE_AGE; // "1 DEAT Y" or "1 DEAT/2 DATE" or "1 DEAT/2 PLAC" - if (preg_match('/\n1 (?:'.WT_EVENTS_DEAT.')(?: Y|(?:\n[2-9].+)*\n2 (DATE|PLAC) )/', $this->_gedrec)) { + if (preg_match('/\n1 (?:'.WT_EVENTS_DEAT.')(?: Y|(?:\n[2-9].+)*\n2 (DATE|PLAC) )/', $this->gedcom)) { return true; } // If any event occured more than $MAX_ALIVE_AGE years ago, then assume the person is dead - if (preg_match_all('/\n2 DATE (.+)/', $this->_gedrec, $date_matches)) { + if (preg_match_all('/\n2 DATE (.+)/', $this->gedcom, $date_matches)) { foreach ($date_matches[1] as $date_match) { $date=new WT_Date($date_match); if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*$MAX_ALIVE_AGE) { @@ -181,7 +175,7 @@ class WT_Person extends WT_GedcomRecord { } // The individual has one or more dated events. All are less than $MAX_ALIVE_AGE years ago. // If one of these is a birth, the person must be alive. - if (preg_match('/\n1 BIRT(?:\n[2-9].+)*\n2 DATE /', $this->_gedrec)) { + if (preg_match('/\n1 BIRT(?:\n[2-9].+)*\n2 DATE /', $this->gedcom)) { return false; } } @@ -192,7 +186,7 @@ class WT_Person extends WT_GedcomRecord { foreach ($this->getChildFamilies(WT_PRIV_HIDE) as $family) { foreach ($family->getSpouses(WT_PRIV_HIDE) as $parent) { // Assume parents are no more than 45 years older than their children - preg_match_all('/\n2 DATE (.+)/', $parent->_gedrec, $date_matches); + preg_match_all('/\n2 DATE (.+)/', $parent->gedcom, $date_matches); foreach ($date_matches[1] as $date_match) { $date=new WT_Date($date_match); if ($date->isOK() && $date->MaxJD() <= WT_SERVER_JD - 365*($MAX_ALIVE_AGE+45)) { @@ -204,7 +198,7 @@ class WT_Person extends WT_GedcomRecord { // Check spouses foreach ($this->getSpouseFamilies(WT_PRIV_HIDE) as $family) { - preg_match_all('/\n2 DATE (.+)/', $family->_gedrec, $date_matches); + preg_match_all('/\n2 DATE (.+)/', $family->gedcom, $date_matches); foreach ($date_matches[1] as $date_match) { $date=new WT_Date($date_match); // Assume marriage occurs after age of 10 @@ -215,7 +209,7 @@ class WT_Person extends WT_GedcomRecord { // Check spouse dates $spouse=$family->getSpouse($this, WT_PRIV_HIDE); if ($spouse) { - preg_match_all('/\n2 DATE (.+)/', $spouse->_gedrec, $date_matches); + preg_match_all('/\n2 DATE (.+)/', $spouse->gedcom, $date_matches); foreach ($date_matches[1] as $date_match) { $date=new WT_Date($date_match); // Assume max age difference between spouses of 40 years @@ -226,7 +220,7 @@ class WT_Person extends WT_GedcomRecord { } // Check child dates foreach ($family->getChildren(WT_PRIV_HIDE) as $child) { - preg_match_all('/\n2 DATE (.+)/', $child->_gedrec, $date_matches); + preg_match_all('/\n2 DATE (.+)/', $child->gedcom, $date_matches); // Assume children born after age of 15 foreach ($date_matches[1] as $date_match) { $date=new WT_Date($date_match); @@ -237,7 +231,7 @@ class WT_Person extends WT_GedcomRecord { // Check grandchildren foreach ($child->getSpouseFamilies(WT_PRIV_HIDE) as $child_family) { foreach ($child_family->getChildren(WT_PRIV_HIDE) as $grandchild) { - preg_match_all('/\n2 DATE (.+)/', $grandchild->_gedrec, $date_matches); + preg_match_all('/\n2 DATE (.+)/', $grandchild->gedcom, $date_matches); // Assume grandchildren born after age of 30 foreach ($date_matches[1] as $date_match) { $date=new WT_Date($date_match); @@ -265,10 +259,10 @@ class WT_Person extends WT_GedcomRecord { $objectC = null; // Iterate over all of the media items for the person - preg_match_all('/\n(\d) OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcomRecord(), $matches, PREG_SET_ORDER); + preg_match_all('/\n(\d) OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcom(), $matches, PREG_SET_ORDER); foreach ($matches as $match) { $media = WT_Media::getInstance($match[2]); - if (!$media || !$media->canDisplayDetails() || $media->isExternal()) { + if (!$media || !$media->canShow() || $media->isExternal()) { continue; } $level = $match[1]; @@ -325,7 +319,7 @@ class WT_Person extends WT_GedcomRecord { */ function getBirthDate() { if (is_null($this->_getBirthDate)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach ($this->getAllBirthDates() as $date) { if ($date->isOK()) { $this->_getBirthDate=$date; @@ -348,7 +342,7 @@ class WT_Person extends WT_GedcomRecord { */ function getBirthPlace() { if (is_null($this->_getBirthPlace)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach ($this->getAllBirthPlaces() as $place) { if ($place) { $this->_getBirthPlace=$place; @@ -371,7 +365,7 @@ class WT_Person extends WT_GedcomRecord { */ function getCensBirthPlace() { if (is_null($this->_getBirthPlace)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach ($this->getAllBirthPlaces() as $place) { if ($place) { $this->_getBirthPlace=$place; @@ -408,7 +402,7 @@ class WT_Person extends WT_GedcomRecord { */ function getDeathDate($estimate = true) { if (is_null($this->_getDeathDate)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach ($this->getAllDeathDates() as $date) { if ($date->isOK()) { $this->_getDeathDate=$date; @@ -431,7 +425,7 @@ class WT_Person extends WT_GedcomRecord { */ function getDeathPlace() { if (is_null($this->_getDeathPlace)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach ($this->getAllDeathPlaces() as $place) { if ($place) { $this->_getDeathPlace=$place; @@ -498,7 +492,7 @@ class WT_Person extends WT_GedcomRecord { // Get all the dates/places for births/deaths - for the INDI lists function getAllBirthDates() { if (is_null($this->_getAllBirthDates)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach (explode('|', WT_EVENTS_BIRT) as $event) { if ($this->_getAllBirthDates=$this->getAllEventDates($event)) { break; @@ -512,7 +506,7 @@ class WT_Person extends WT_GedcomRecord { } function getAllBirthPlaces() { if (is_null($this->_getAllBirthPlaces)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach (explode('|', WT_EVENTS_BIRT) as $event) { if ($this->_getAllBirthPlaces=$this->getAllEventPlaces($event)) { break; @@ -526,7 +520,7 @@ class WT_Person extends WT_GedcomRecord { } function getAllDeathDates() { if (is_null($this->_getAllDeathDates)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach (explode('|', WT_EVENTS_DEAT) as $event) { if ($this->_getAllDeathDates=$this->getAllEventDates($event)) { break; @@ -540,7 +534,7 @@ class WT_Person extends WT_GedcomRecord { } function getAllDeathPlaces() { if (is_null($this->_getAllDeathPlaces)) { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { foreach (explode('|', WT_EVENTS_DEAT) as $event) { if ($this->_getAllDeathPlaces=$this->getAllEventPlaces($event)) { break; @@ -664,7 +658,7 @@ class WT_Person extends WT_GedcomRecord { // the privatize-gedcom function, and we are allowed to know this. function getSex() { if (is_null($this->sex)) { - if (preg_match('/\n1 SEX ([MF])/', $this->_gedrec, $match)) { + if (preg_match('/\n1 SEX ([MF])/', $this->gedcom, $match)) { $this->sex=$match[1]; } else { $this->sex='U'; @@ -753,7 +747,7 @@ class WT_Person extends WT_GedcomRecord { if ($access_level==WT_PRIV_HIDE) { // special case, (temporary - cannot make this generic as other code depends on the private cached values) $families=array(); - preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $this->_gedrec, $match); + preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $this->gedcom, $match); foreach ($match[1] as $pid) { $family=WT_Family::getInstance($pid); if ($family) { @@ -765,10 +759,10 @@ class WT_Person extends WT_GedcomRecord { if ($this->_spouseFamilies===null) { $this->_spouseFamilies=array(); - preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $this->_gedrec, $match); + preg_match_all('/\n1 FAMS @('.WT_REGEX_XREF.')@/', $this->gedcom, $match); foreach ($match[1] as $pid) { $family=WT_Family::getInstance($pid); - if ($family && ($SHOW_PRIVATE_RELATIONSHIPS || $family->canDisplayDetails($access_level))) { + if ($family && ($SHOW_PRIVATE_RELATIONSHIPS || $family->canShow($access_level))) { $this->_spouseFamilies[]=$family; } } @@ -794,7 +788,7 @@ class WT_Person extends WT_GedcomRecord { // Get a count of the children for this individual function getNumberOfChildren() { - if (preg_match('/\n1 NCHI (\d+)(?:\n|$)/', $this->getGedcomRecord(), $match)) { + if (preg_match('/\n1 NCHI (\d+)(?:\n|$)/', $this->getGedcom(), $match)) { return $match[1]; } else { $children=array(); @@ -814,7 +808,7 @@ class WT_Person extends WT_GedcomRecord { if ($access_level==WT_PRIV_HIDE) { // special case, (temporary - cannot make this generic as other code depends on the private cached values) $families=array(); - preg_match_all('/\n1 FAMC @('.WT_REGEX_XREF.')@/', $this->_gedrec, $match); + preg_match_all('/\n1 FAMC @('.WT_REGEX_XREF.')@/', $this->gedcom, $match); foreach ($match[1] as $pid) { $family=WT_Family::getInstance($pid); if ($family) { @@ -826,10 +820,10 @@ class WT_Person extends WT_GedcomRecord { if ($this->_childFamilies===null) { $this->_childFamilies=array(); - preg_match_all('/\n1 FAMC @('.WT_REGEX_XREF.')@/', $this->_gedrec, $match); + preg_match_all('/\n1 FAMC @('.WT_REGEX_XREF.')@/', $this->gedcom, $match); foreach ($match[1] as $pid) { $family=WT_Family::getInstance($pid); - if ($family && ($SHOW_PRIVATE_RELATIONSHIPS || $family->canDisplayDetails($access_level))) { + if ($family && ($SHOW_PRIVATE_RELATIONSHIPS || $family->canShow($access_level))) { $this->_childFamilies[]=$family; } } @@ -852,19 +846,19 @@ class WT_Person extends WT_GedcomRecord { // If there is more than one FAMC record, choose the preferred parents: // a) records with '2 _PRIMARY' foreach ($families as $famid=>$fam) { - if (preg_match("/\n1 FAMC @{$famid}@\n(?:[2-9].*\n)*(?:2 _PRIMARY Y)/", $this->getGedcomRecord())) { + if (preg_match("/\n1 FAMC @{$famid}@\n(?:[2-9].*\n)*(?:2 _PRIMARY Y)/", $this->getGedcom())) { return $fam; } } // b) records with '2 PEDI birt' foreach ($families as $famid=>$fam) { - if (preg_match("/\n1 FAMC @{$famid}@\n(?:[2-9].*\n)*(?:2 PEDI birth)/", $this->getGedcomRecord())) { + if (preg_match("/\n1 FAMC @{$famid}@\n(?:[2-9].*\n)*(?:2 PEDI birth)/", $this->getGedcom())) { return $fam; } } // c) records with no '2 PEDI' foreach ($families as $famid=>$fam) { - if (!preg_match("/\n1 FAMC @{$famid}@\n(?:[2-9].*\n)*(?:2 PEDI)/", $this->getGedcomRecord())) { + if (!preg_match("/\n1 FAMC @{$famid}@\n(?:[2-9].*\n)*(?:2 PEDI)/", $this->getGedcom())) { return $fam; } } @@ -877,7 +871,7 @@ class WT_Person extends WT_GedcomRecord { * @return string FAMC:PEDI value [ adopted | birth | foster | sealing ] */ function getChildFamilyPedigree($famid) { - $subrec = get_sub_record(1, '1 FAMC @'.$famid.'@', $this->getGedcomRecord()); + $subrec = get_sub_record(1, '1 FAMC @'.$famid.'@', $this->getGedcom()); $pedi = get_gedcom_value('PEDI', 2, $subrec); // birth=default => return an empty string return ($pedi=='birth') ? '' : $pedi; @@ -925,35 +919,9 @@ class WT_Person extends WT_GedcomRecord { return $step_families; } - /** - * get global facts - * @return array - */ - function getGlobalFacts() { - $this->parseFacts(); - return $this->globalfacts; - } - /** - * get indi facts - * @return array - */ - function getIndiFacts() { - $this->parseFacts(); - return $this->indifacts; - } - - /** - * get other facts - * @return array - */ - function getOtherFacts() { - $this->parseFacts(); - return $this->otherfacts; - } - // A label for a parental family group function getChildFamilyLabel(WT_Family $family) { - if (preg_match('/\n1 FAMC @'.$family->getXref().'@(?:\n[2-9].*)*\n2 PEDI (.+)/', $this->getGedcomRecord(), $match)) { + if (preg_match('/\n1 FAMC @'.$family->getXref().'@(?:\n[2-9].*)*\n2 PEDI (.+)/', $this->getGedcom(), $match)) { // A specified pedigree return WT_Gedcom_Code_Pedi::getChildFamilyLabel($match[1]); } else { @@ -1001,8 +969,9 @@ class WT_Person extends WT_GedcomRecord { } } // It should not be possible to get here - throw new Exception('Invalid family in WT_Person::getStepFamilyLabel(' . $family . ')'); + throw new Exception('Invalid family in WT_Individual::getStepFamilyLabel(' . $family . ')'); } + /** * get the correct label for a family * @param Family $family the family to get the label for @@ -1025,595 +994,6 @@ class WT_Person extends WT_GedcomRecord { // I18N: %s is the spouse name return WT_I18N::translate('Family with %s', $spouse); } - /** - * get updated Person - * If there is an updated individual record in the gedcom file - * return a new person object for it - * @return Person - */ - function getUpdatedPerson() { - if ($this->getChanged()) { - return null; - } - if (WT_USER_CAN_EDIT && $this->canDisplayDetails()) { - $newrec = find_updated_record($this->xref, $this->ged_id); - if (!is_null($newrec)) { - $new = new WT_Person($newrec); - $new->setChanged(true); - return $new; - } - } - return null; - } - /** - * Parse the facts from the individual record - */ - function parseFacts() { - parent::parseFacts(); - //-- only run this function once - if ($this->facts_parsed) return; - //-- don't run this function if privacy does not allow viewing of details - if (!$this->canDisplayDetails()) return; - $sexfound = false; - //-- run the parseFacts() method from the parent class - $this->facts_parsed = true; - - //-- sort the fact info into different categories for people - foreach ($this->facts as $f=>$event) { - $fact = $event->getTag(); - if ($fact=='NAME') { - // -- handle special name fact case - $this->globalfacts[] = $event; - } elseif ($fact=='SOUR') { - // -- handle special source fact case - $this->otherfacts[] = $event; - } elseif ($fact=='NOTE') { - // -- handle special note fact case - $this->otherfacts[] = $event; - } elseif ($fact=='SEX') { - // -- handle special sex case - $this->globalfacts[] = $event; - $sexfound = true; - } elseif ($fact=='OBJE') { - } else { - $this->indifacts[] = $event; - } - } - //-- add a new sex fact if one was not found - if (!$sexfound) { - $this->globalfacts[] = new WT_Event('1 SEX U', $this, 'new'); - } - } - /** - * add facts from the family record - * @param boolean $otherfacts whether or not to add other related facts such as parents facts, associated people facts, and historical facts - */ - function add_family_facts($otherfacts = true) { - global $GEDCOM, $nonfacts, $nonfamfacts; - - if (!isset($nonfacts)) $nonfacts = array(); - if (!isset($nonfamfacts)) $nonfamfacts = array(); - - if (!$this->canDisplayDetails()) return; - $this->parseFacts(); - //-- Get the facts from the family with spouse (FAMS) - foreach ($this->getSpouseFamilies() as $family) { - $updfamily = $family->getUpdatedFamily(); //-- updated family ? - $spouse = $family->getSpouse($this); - - if ($updfamily) { - $family->diffMerge($updfamily); - } - $facts = $family->getFacts(); - $hasdiv = false; - /* @var $event WT_Event */ - foreach ($facts as $event) { - $fact = $event->getTag(); - if ($fact=='DIV') $hasdiv = true; - // -- handle special source fact case - if ($fact!='SOUR' && $fact!='NOTE' && $fact!='CHAN' && $fact!='_UID' && $fact!='RIN') { - if (!in_array($fact, $nonfacts) && !in_array($fact, $nonfamfacts)) { - $event->setSpouse($spouse); - if ($fact!='OBJE') { - $this->indifacts[]=$event; - } else { - $this->otherfacts[]=$event; - } - } - } - } - if ($otherfacts) { - if (!$hasdiv && !is_null($spouse)) $this->add_spouse_facts($spouse, $family->getGedcomRecord()); - $this->add_children_facts($family, '_CHIL', ''); - } - } - if ($otherfacts) { - $this->add_parents_facts($this, 1); - $this->add_historical_facts(); - $this->add_asso_facts(); - } - } - - // Add parents' (and parents' relatives') events to individual facts array - function add_parents_facts($person, $sosa) { - global $SHOW_RELATIVES_EVENTS; - - switch ($sosa) { - case 1: - foreach ($person->getChildFamilies() as $family) { - // Add siblings - $this->add_children_facts($family, '_SIBL', ''); - foreach ($family->getSpouses() as $spouse) { - foreach ($spouse->getSpouseFamilies() as $sfamily) { - if (!$family->equals($sfamily)) { - // Add half-siblings - $this->add_children_facts($sfamily, '_HSIB', 'par'); - } - } - // Add grandparents - $this->add_parents_facts($spouse, $spouse->getSex()=='F' ? 3 : 2); - } - } - - $rela=''; - break; - case 2: - $rela='fat'; - break; - case 3: - $rela='mot'; - break; - } - - // Only include events between birth and death - $bDate=$this->getEstimatedBirthDate(); - $dDate=$this->getEstimatedDeathDate(); - - foreach ($person->getChildFamilies() as $famid=>$family) { - foreach ($family->getSpouses() as $parent) { - if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT'.($sosa==1 ? '_PARE' : '_GPAR'))) { - foreach ($parent->getAllFactsByType(explode('|', WT_EVENTS_DEAT)) as $sEvent) { - if ($sEvent->getDate()->isOK() && WT_Date::Compare($bDate, $sEvent->getDate())<=0 && WT_Date::Compare($sEvent->getDate(), $dDate)<=0) { - switch ($sosa) { - case 1: - // Convert the event to a close relatives event - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1_PARE ', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_PARE\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - break; - case 2: - // Convert the event to a close relatives event - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1_GPA1 ', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_GPA1\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - break; - case 3: - // Convert the event to a close relatives event - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1_GPA2 ', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_GPA2\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - break; - } - // Create a new event - $this->indifacts[]=new WT_Event($tmp_rec."\n2 _ASSO @".$parent->getXref()."@", $parent, 0); - } - } - } - } - - if ($sosa==1 && strstr($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) { - // add father/mother marriages - foreach ($family->getSpouses() as $parent) { - foreach ($parent->getSpouseFamilies() as $sfamily) { - foreach ($sfamily->getAllFactsByType(explode('|', WT_EVENTS_MARR)) as $sEvent) { - if ($sEvent->getDate()->isOK() && WT_Date::Compare($bDate, $sEvent->getDate())<=0 && WT_Date::Compare($sEvent->getDate(), $dDate)<=0) { - if ($sfamily->equals($family)) { - if ($parent->getSex()=='F') { - // show current family marriage only once - continue; - } - // marriage of parents (to each other) - // Convert the event to a close relatives event - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_MARR.')/', '1 _$1_FAMC ', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_FAMC\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } else { - // marriage of a parent (to another spouse) - // Convert the event to a close relatives event - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_MARR.')/', '1 _$1_PARE ', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_PARE\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } - // Add links to both spouses - foreach ($sfamily->getSpouses() as $spouse) { - $tmp_rec .= "\n2 _ASSO @" . $spouse->getXref() . '@'; - } - // Create a new event - $tmp = new WT_Event($tmp_rec, $sfamily, 0); - if (!$sfamily->equals($family)) { - $tmp->setSpouse($parent); - } - $this->indifacts[]=$tmp; - } - } - } - } - } - } - } - - /** - * add children events to individual facts array - * - * @param string $family Family object - * @param string $option Family level indicator - * @param string $relation Relationship path indicator - * @return records added to indifacts array - */ - function add_children_facts($family, $option, $relation) { - global $SHOW_RELATIVES_EVENTS; - - // Deal with recursion. - switch ($option) { - case '_CHIL': - // Add grandchildren - foreach ($family->getChildren() as $child) { - foreach ($child->getSpouseFamilies() as $cfamily) { - switch ($child->getSex()) { - case 'M': - $this->add_children_facts($cfamily, '_GCHI', 'son'); - break; - case 'F': - $this->add_children_facts($cfamily, '_GCHI', 'dau'); - break; - case 'U': - $this->add_children_facts($cfamily, '_GCHI', 'chi'); - break; - } - } - } - break; - } - - // For each child in the family - foreach ($family->getChildren() as $child) { - if ($child->getXref()==$this->getXref()) { - // We are not our own sibling! - continue; - } - // add child's birth - if (strpos($SHOW_RELATIVES_EVENTS, '_BIRT'.str_replace('_HSIB', '_SIBL', $option))!==false) { - foreach ($child->getAllFactsByType(explode('|', WT_EVENTS_BIRT)) as $sEvent) { - $sgdate=$sEvent->getDate(); - // Always show _BIRT_CHIL, even if the dates are not known - if ($option=='_CHIL' || $sgdate->isOK() && WT_Date::Compare($this->getEstimatedBirthDate(), $sgdate)<=0 && WT_Date::Compare($sgdate, $this->getEstimatedDeathDate())<=0) { - if ($option=='_GCHI' && $relation=='dau') { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_BIRT.')/', '1 _$1_GCH1', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_GCH1\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } elseif ($option=='_GCHI' && $relation=='son') { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_BIRT.')/', '1 _$1_GCH2', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_GCH2\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } else { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_BIRT.')/', '1 _$1'.$option, $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag().$option."\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } - $event=new WT_Event($tmp_rec."\n2 _ASSO @".$child->getXref()."@", $child, 0); - if (!in_array($event, $this->indifacts)) { - $this->indifacts[]=$event; - } - } - } - } - // add child's death - if (strpos($SHOW_RELATIVES_EVENTS, '_DEAT'.str_replace('_HSIB', '_SIBL', $option))!==false) { - foreach ($child->getAllFactsByType(explode('|', WT_EVENTS_DEAT)) as $sEvent) { - $sgdate=$sEvent->getDate(); - $srec = $sEvent->getGedcomRecord(); - if ($sgdate->isOK() && WT_Date::Compare($this->getEstimatedBirthDate(), $sgdate)<=0 && WT_Date::Compare($sgdate, $this->getEstimatedDeathDate())<=0) { - if ($option=='_GCHI' && $relation=='dau') { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1_GCH1', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_GCH1\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } elseif ($option=='_GCHI' && $relation=='son') { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1_GCH2', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_GCH2\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } else { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1'.$option, $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag().$option."\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } - $event=new WT_Event($tmp_rec."\n2 _ASSO @".$child->getXref()."@", $child, 0); - if (!in_array($event, $this->indifacts)) { - $this->indifacts[]=$event; - } - } - } - } - // add child's marriage - if (strstr($SHOW_RELATIVES_EVENTS, '_MARR'.str_replace('_HSIB', '_SIBL', $option))) { - foreach ($child->getSpouseFamilies() as $sfamily) { - foreach ($sfamily->getAllFactsByType(explode('|', WT_EVENTS_MARR)) as $sEvent) { - $sgdate=$sEvent->getDate(); - if ($sgdate->isOK() && WT_Date::Compare($this->getEstimatedBirthDate(), $sgdate)<=0 && WT_Date::Compare($sgdate, $this->getEstimatedDeathDate())<=0) { - if ($option=='_GCHI' && $relation=='dau') { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_MARR.')/', '1 _$1_GCH1', $sEvent->getGedcomRecord()); - $tmp_rec="1 _".$sEvent->getTag()."_GCH1\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } elseif ($option=='_GCHI' && $relation=='son') { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_MARR.')/', '1 _$1_GCH2', $sEvent->getGedcomRecord()); - $tmp_rec="1 _".$sEvent->getTag()."_GCH2\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } else { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_MARR.')/', '1 _$1'.$option, $sEvent->getGedcomRecord()); - $tmp_rec="1 _".$sEvent->getTag().$option."\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - } - // Add links to both spouses - foreach ($sfamily->getSpouses() as $spouse) { - $tmp_rec .= "\n2 _ASSO @" . $spouse->getXref() . '@'; - } - // Create a new event - $tmp = new WT_Event($tmp_rec, $sfamily, 0); - $tmp->setSpouse($child); - $this->indifacts[]=$tmp; - } - } - } - } - } - } - /** - * add spouse events to individual facts array - * - * bdate = indi birth date record - * ddate = indi death date record - * - * @param string $spouse Person object - * @param string $famrec family Gedcom record - * @return records added to indifacts array - */ - function add_spouse_facts($spouse, $famrec='') { - global $SHOW_RELATIVES_EVENTS; - - // do not show if divorced - if (preg_match('/\n1 (?:'.WT_EVENTS_DIV.')\b/', $famrec)) { - return; - } - // Only include events between birth and death - $bDate=$this->getEstimatedBirthDate(); - $dDate=$this->getEstimatedDeathDate(); - - // add spouse death - if ($spouse && strstr($SHOW_RELATIVES_EVENTS, '_DEAT_SPOU')) { - foreach ($spouse->getAllFactsByType(explode('|', WT_EVENTS_DEAT)) as $sEvent) { - $sdate=$sEvent->getDate(); - if ($sdate->isOK() && WT_Date::Compare($this->getEstimatedBirthDate(), $sdate)<=0 && WT_Date::Compare($sdate, $this->getEstimatedDeathDate())<=0) { - // Convert the event to a close relatives event. - $tmp_rec=preg_replace('/^1 ('.WT_EVENTS_DEAT.')/', '1 _$1_SPOU ', $sEvent->getGedcomRecord()); // Full - $tmp_rec="1 _".$sEvent->getTag()."_SPOU\n2 DATE ".$sEvent->getValue('DATE')."\n2 PLAC ".$sEvent->getValue('PLAC'); // Abbreviated - // Create a new event - $this->indifacts[]=new WT_Event($tmp_rec."\n2 _ASSO @".$spouse->getXref()."@", $spouse, 0); - } - } - } - } - - /** - * add historical events to individual facts array - * - * @return records added to indifacts array - * - * Historical facts are imported from optional language file : histo.xx.php - * where xx is language code - * This file should contain records similar to : - * - * $histo[]="1 EVEN\n2 TYPE History\n2 DATE 11 NOV 1918\n2 NOTE WW1 Armistice"; - * $histo[]="1 EVEN\n2 TYPE History\n2 DATE 8 MAY 1945\n2 NOTE WW2 Armistice"; - * etc... - * - */ - function add_historical_facts() { - global $SHOW_RELATIVES_EVENTS; - if (!$SHOW_RELATIVES_EVENTS) return; - - // Only include events between birth and death - $bDate=$this->getEstimatedBirthDate(); - $dDate=$this->getEstimatedDeathDate(); - if (!$bDate->isOK()) return; - - if (file_exists(WT_Site::preference('INDEX_DIRECTORY').'histo.'.WT_LOCALE.'.php')) { - require WT_Site::preference('INDEX_DIRECTORY').'histo.'.WT_LOCALE.'.php'; - foreach ($histo as $indexval=>$hrec) { - $sdate=new WT_Date(get_gedcom_value('DATE', 2, $hrec)); - if ($sdate->isOK() && WT_Date::Compare($this->getEstimatedBirthDate(), $sdate)<=0 && WT_Date::Compare($sdate, $this->getEstimatedDeathDate())<=0) { - $event = new WT_Event($hrec, null, -1); - $this->indifacts[] = $event; - } - } - } - } - /** - * add events where pid is an ASSOciate - * - * @return records added to indifacts array - * - */ - function add_asso_facts() { - $associates=array_merge( - fetch_linked_indi($this->getXref(), 'ASSO', $this->ged_id), - fetch_linked_indi($this->getXref(), '_ASSO', $this->ged_id), - fetch_linked_fam ($this->getXref(), 'ASSO', $this->ged_id), - fetch_linked_fam ($this->getXref(), '_ASSO', $this->ged_id) - ); - foreach ($associates as $associate) { - foreach ($associate->getFacts() as $event) { - $srec = $event->getGedcomRecord(); - foreach (array('ASSO', '_ASSO') as $asso_tag) { - $arec = get_sub_record(2, '2 ' . $asso_tag . ' @' . $this->getXref() . '@', $srec); - if ($arec) { - // Extract the important details from the fact - $factrec='1 '.$event->getTag(); - if (preg_match('/\n2 DATE .*/', $srec, $match)) { - $factrec.=$match[0]; - } - if (preg_match('/\n2 PLAC .*/', $srec, $match)) { - $factrec.=$match[0]; - } - if ($associate instanceof WT_Family) { - foreach ($associate->getSpouses() as $spouse) { - $factrec.="\n2 $asso_tag @".$spouse->getXref().'@'; - } - } else { - $factrec.="\n2 $asso_tag @".$associate->getXref().'@'; - // CHR/BAPM events are commonly used. Generate the reverse relationship - if (preg_match('/^(?:BAPM|CHR)$/', $event->getTag()) && preg_match('/3 RELA god(?:parent|mother|father)/', $event->getGedcomRecord())) { - switch ($associate->getSex()) { - case 'M': - $factrec.="\n3 RELA godson"; - break; - case 'F': - $factrec.="\n3 RELA goddaughter"; - break; - case 'U': - $factrec.="\n3 RELA godchild"; - break; - } - } - } - $this->indifacts[] = new WT_Event($factrec, $associate, 0); - } - } - } - } - } - - /** - * Merge the facts from another Person object into this object - * for generating a diff view - * @param Person $diff the person to compare facts with - */ - function diffMerge($diff) { - if (is_null($diff)) return; - $this->parseFacts(); - $diff->parseFacts(); - //-- loop through new facts and add them to the list if they are any changes - //-- compare new and old facts of the Personal Fact and Details tab 1 - for ($i=0; $i<count($this->indifacts); $i++) { - $found=false; - $oldfactrec = $this->indifacts[$i]->getGedcomRecord(); - foreach ($diff->indifacts as $newfact) { - $newfactrec = $newfact->getGedcomRecord(); - //-- remove all whitespace for comparison - $tnf = preg_replace('/\s+/', ' ', $newfactrec); - $tif = preg_replace('/\s+/', ' ', $oldfactrec); - if ($tnf==$tif) { - $this->indifacts[$i] = $newfact; //-- make sure the correct linenumber is used - $found=true; - break; - } - } - //-- fact was deleted? - if (!$found) { - $this->indifacts[$i]->setIsOld(); - } - } - //-- check for any new facts being added - foreach ($diff->indifacts as $newfact) { - $found=false; - foreach ($this->indifacts as $fact) { - $tif = preg_replace('/\s+/', ' ', $fact->getGedcomRecord()); - $tnf = preg_replace('/\s+/', ' ', $newfact->getGedcomRecord()); - if ($tif==$tnf) { - $found=true; - break; - } - } - if (!$found) { - $newfact->setIsNew(); - $this->indifacts[]=$newfact; - } - } - //-- compare new and old facts of the Notes Sources and Media tab 2 - for ($i=0; $i<count($this->otherfacts); $i++) { - $found=false; - foreach ($diff->otherfacts as $newfact) { - if (trim($newfact->getGedcomRecord())==trim($this->otherfacts[$i]->getGedcomRecord())) { - $this->otherfacts[$i] = $newfact; //-- make sure the correct linenumber is used - $found=true; - break; - } - } - if (!$found) { - $this->otherfacts[$i]->setIsOld(); - } - } - foreach ($diff->otherfacts as $indexval => $newfact) { - $found=false; - foreach ($this->otherfacts as $indexval => $fact) { - if (trim($fact->getGedcomRecord())==trim($newfact->getGedcomRecord())) { - $found=true; - break; - } - } - if (!$found) { - $newfact->setIsNew(); - $this->otherfacts[]=$newfact; - } - } - - //-- compare new and old facts of the Global facts - for ($i=0; $i<count($this->globalfacts); $i++) { - $found=false; - foreach ($diff->globalfacts as $indexval => $newfact) { - if (trim($newfact->getGedcomRecord())==trim($this->globalfacts[$i]->getGedcomRecord())) { - $this->globalfacts[$i] = $newfact; //-- make sure the correct linenumber is used - $found=true; - break; - } - } - if (!$found) { - $this->globalfacts[$i]->setIsOld(); - } - } - foreach ($diff->globalfacts as $indexval => $newfact) { - $found=false; - foreach ($this->globalfacts as $indexval => $fact) { - if (trim($fact->getGedcomRecord())==trim($newfact->getGedcomRecord())) { - $found=true; - break; - } - } - if (!$found) { - $newfact->setIsNew(); - $this->globalfacts[]=$newfact; - } - } - - foreach ($diff->getChildFamilies() as $diff_family) { - $exists=false; - foreach ($this->getChildFamilies() as $family) { - if ($family->equals($diff_family)) { - $exists=true; - break; - } - } - if (!$exists) { - $this->_childFamilies[]=$diff_family; - } - } - - foreach ($diff->getSpouseFamilies() as $diff_family) { - $exists=false; - foreach ($this->getSpouseFamilies() as $family) { - if ($family->equals($diff_family)) { - $exists=true; - break; - } - } - if (!$exists) { - $this->_spouseFamilies[]=$diff_family; - } - } - } /** * get primary parents names for this person @@ -1677,19 +1057,19 @@ class WT_Person extends WT_GedcomRecord { // 1 NAME Carlos /Vasquez y Sante/ // 2 GIVN Carlos // 2 SURN Vasquez,Sante - protected function _addName($type, $full, $gedrec) { + protected function _addName($type, $full, $gedcom) { global $UNKNOWN_NN, $UNKNOWN_PN; //////////////////////////////////////////////////////////////////////////// // Extract the structured name parts - use for "sortable" names and indexes //////////////////////////////////////////////////////////////////////////// - $sublevel=1+(int)$gedrec[0]; - $NPFX=preg_match("/\n{$sublevel} NPFX (.+)/", $gedrec, $match) ? $match[1] : ''; - $GIVN=preg_match("/\n{$sublevel} GIVN (.+)/", $gedrec, $match) ? $match[1] : ''; - $SURN=preg_match("/\n{$sublevel} SURN (.+)/", $gedrec, $match) ? $match[1] : ''; - $NSFX=preg_match("/\n{$sublevel} NSFX (.+)/", $gedrec, $match) ? $match[1] : ''; - $NICK=preg_match("/\n{$sublevel} NICK (.+)/", $gedrec, $match) ? $match[1] : ''; + $sublevel=1+(int)$gedcom[0]; + $NPFX=preg_match("/\n{$sublevel} NPFX (.+)/", $gedcom, $match) ? $match[1] : ''; + $GIVN=preg_match("/\n{$sublevel} GIVN (.+)/", $gedcom, $match) ? $match[1] : ''; + $SURN=preg_match("/\n{$sublevel} SURN (.+)/", $gedcom, $match) ? $match[1] : ''; + $NSFX=preg_match("/\n{$sublevel} NSFX (.+)/", $gedcom, $match) ? $match[1] : ''; + $NICK=preg_match("/\n{$sublevel} NICK (.+)/", $gedcom, $match) ? $match[1] : ''; // SURN is an comma-separated list of surnames... if ($SURN) { @@ -1867,7 +1247,7 @@ class WT_Person extends WT_GedcomRecord { } else { $char = ($bwidth/6.5); } - if ($this->canDisplayName()) { + if ($this->canShowName()) { $tmp=$this->getAllNames(); $givn = $tmp[$this->getPrimaryName()]['givn']; $surn = $tmp[$this->getPrimaryName()]['surname']; diff --git a/library/WT/Media.php b/library/WT/Media.php index e370da631b..23643184df 100644 --- a/library/WT/Media.php +++ b/library/WT/Media.php @@ -30,62 +30,55 @@ if (!defined('WT_WEBTREES')) { class WT_Media extends WT_GedcomRecord { const RECORD_TYPE = 'OBJE'; + const SQL_FETCH = "SELECT m_gedcom FROM `##media` WHERE m_id=? AND m_file=?"; const URL_PREFIX = 'mediaviewer.php?mid='; public $title = null; // TODO: these should be private, with getTitle() and getFilename() functions public $file = null; // Create a Media object from either raw GEDCOM data or a database row - public function __construct($data) { - parent::__construct($data); + public function __construct($xref, $gedcom, $pending, $gedcom_id) { + parent::__construct($xref, $gedcom, $pending, $gedcom_id); - if (is_array($data)) { - // Construct from a row from the database - $this->file =$data['m_filename']; - $this->title=$data['m_titl']; + // TODO get this data from WT_Fact objects + if (preg_match('/\n1 FILE (.+)/', $gedcom.$pending, $match)) { + $this->file = $match[1]; } else { - // Construct from raw GEDCOM data - if (preg_match('/\n1 FILE (.+)/', $data, $match)) { - $this->file = $match[1]; - } else { - $this->file = ''; - } - if (preg_match('/\n\d TITL (.+)/', $data, $match)) { - $this->title = $match[1]; - } else { - $this->title = $this->file; - } + $this->file = ''; + } + if (preg_match('/\n\d TITL (.+)/', $gedcom.$pending, $match)) { + $this->title = $match[1]; + } else { + $this->title = $this->file; } } // Implement media-specific privacy logic ... - protected function _canDisplayDetailsByType($access_level) { + protected function _canShowByType($access_level) { // Hide media objects if they are attached to private records $linked_ids=WT_DB::prepare( "SELECT l_from FROM `##link` WHERE l_to=? AND l_file=?" - )->execute(array($this->xref, $this->ged_id))->fetchOneColumn(); + )->execute(array($this->xref, $this->gedcom_id))->fetchOneColumn(); foreach ($linked_ids as $linked_id) { $linked_record=WT_GedcomRecord::getInstance($linked_id); - if ($linked_record && !$linked_record->canDisplayDetails($access_level)) { + if ($linked_record && !$linked_record->canShow($access_level)) { return false; } } // ... otherwise apply default behaviour - return parent::_canDisplayDetailsByType($access_level); + return parent::_canShowByType($access_level); } // Fetch the record from the database - protected static function fetchGedcomRecord($xref, $ged_id) { + protected static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; if ($statement===null) { - $statement=WT_DB::prepare( - "SELECT 'OBJE' AS type, m_id AS xref, m_file AS ged_id, m_gedcom AS gedrec, m_titl, m_filename". - " FROM `##media` WHERE m_id=? AND m_file=?" - ); + $statement=WT_DB::prepare("SELECT m_gedcom FROM `##media` WHERE m_id=? AND m_file=?"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } /** @@ -93,7 +86,7 @@ class WT_Media extends WT_GedcomRecord { * @return string */ public function getNote() { - return get_gedcom_value('NOTE', 1, $this->getGedcomRecord()); + return get_gedcom_value('NOTE', 1, $this->getGedcom()); } /** @@ -246,13 +239,13 @@ class WT_Media extends WT_GedcomRecord { * @return string */ public function getMediaType() { - $mediaType = strtolower(get_gedcom_value('FORM:TYPE', 2, $this->getGedcomRecord())); + $mediaType = strtolower(get_gedcom_value('FORM:TYPE', 2, $this->getGedcom())); return $mediaType; } // Is this object marked as a highlighted image? public function isPrimary() { - if (preg_match('/\n\d _PRIM ([YN])/', $this->getGedcomRecord(), $match)) { + if (preg_match('/\n\d _PRIM ([YN])/', $this->getGedcom(), $match)) { return $match[1]; } else { return ''; @@ -341,7 +334,7 @@ class WT_Media extends WT_GedcomRecord { $downloadstr = ($download) ? '&dl=1' : ''; return 'mediafirewall.php?mid=' . $this->getXref() . $thumbstr . $downloadstr . - '&ged=' . rawurlencode(get_gedcom_from_id($this->ged_id)) . + '&ged=' . rawurlencode(get_gedcom_from_id($this->gedcom_id)) . '&cb=' . $this->getEtag($which); } @@ -425,7 +418,7 @@ class WT_Media extends WT_GedcomRecord { // If this object has no name, what do we call it? public function getFallBackName() { - if ($this->canDisplayDetails()) { + if ($this->canShow()) { return basename($this->file); } else { return $this->getXref(); @@ -434,7 +427,7 @@ class WT_Media extends WT_GedcomRecord { // Get an array of structures containing all the names in the record public function getAllNames() { - if (strpos($this->getGedcomRecord(), "\n1 TITL ")) { + if (strpos($this->getGedcom(), "\n1 TITL ")) { // Earlier gedcom versions had level 1 titles return parent::_getAllNames('TITL', 1); } else { diff --git a/library/WT/MenuBar.php b/library/WT/MenuBar.php index 1cc5d647b7..0798f4cafc 100644 --- a/library/WT/MenuBar.php +++ b/library/WT/MenuBar.php @@ -2,7 +2,7 @@ // System for generating menus. // // webtrees: Web based Family History software -// Copyright (C) 2012 webtrees development team. +// Copyright (C) 2013 webtrees development team. // // Derived from PhpGedView // Copyright (C) 2002 to 2010 PGV Development Team. All rights reserved. @@ -235,8 +235,8 @@ class WT_MenuBar { // Add a submenu showing relationship from this person to each of our favorites foreach (user_favorites_WT_Module::getFavorites(WT_USER_ID) as $favorite) { if ($favorite['type']=='INDI' && $favorite['gedcom_id']==WT_GED_ID) { - $person=WT_Person::getInstance($favorite['gid']); - if ($person instanceof WT_Person) { + $person=WT_Individual::getInstance($favorite['gid']); + if ($person instanceof WT_Individual) { $subsubmenu = new WT_Menu( $person->getFullName(), 'relationship.php?pid1='.$person->getXref().'&pid2='.$pid2.'&ged='.WT_GEDURL, @@ -517,7 +517,7 @@ class WT_MenuBar { case 'OBJE': case 'NOTE': $obj=WT_GedcomRecord::getInstance($favorite['gid']); - if ($obj && $obj->canDisplayName()) { + if ($obj && $obj->canShowName()) { $submenu=new WT_Menu($obj->getFullName(), $obj->getHtmlUrl()); $menu->addSubMenu($submenu); } diff --git a/library/WT/Note.php b/library/WT/Note.php index 0f1cedafa0..c9524b286f 100644 --- a/library/WT/Note.php +++ b/library/WT/Note.php @@ -30,23 +30,33 @@ if (!defined('WT_WEBTREES')) { class WT_Note extends WT_GedcomRecord { const RECORD_TYPE = 'NOTE'; + const SQL_FETCH = "SELECT o_gedcom FROM `##other` WHERE o_id=? AND o_file=?"; const URL_PREFIX = 'note.php?nid='; + // Get the text contents of the note + public function getNote() { + if (preg_match('/^0 @' . WT_REGEX_TAG . '@ NOTE ?(.*(?:\n1 CONT .*)*)/', $this->gedcom, $match)) { + return str_replace("\n1 CONT ", "\n", $match[1]); + } else { + return null; + } + } + // Implement note-specific privacy logic - protected function _canDisplayDetailsByType($access_level) { + protected function _canShowByType($access_level) { // Hide notes if they are attached to private records $linked_ids=WT_DB::prepare( "SELECT l_from FROM `##link` WHERE l_to=? AND l_file=?" - )->execute(array($this->xref, $this->ged_id))->fetchOneColumn(); + )->execute(array($this->xref, $this->gedcom_id))->fetchOneColumn(); foreach ($linked_ids as $linked_id) { $linked_record=WT_GedcomRecord::getInstance($linked_id); - if ($linked_record && !$linked_record->canDisplayDetails($access_level)) { + if ($linked_record && !$linked_record->canShow($access_level)) { return false; } } // Apply default behaviour - return parent::_canDisplayDetailsByType($access_level); + return parent::_canShowByType($access_level); } // Generate a private version of this record @@ -55,16 +65,14 @@ class WT_Note extends WT_GedcomRecord { } // Fetch the record from the database - protected static function fetchGedcomRecord($xref, $ged_id) { + protected static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; if ($statement===null) { - $statement=WT_DB::prepare( - "SELECT o_type AS type, o_id AS xref, o_file AS ged_id, o_gedcom AS gedrec ". - "FROM `##other` WHERE o_id=? AND o_file=? AND o_type='NOTE'" - ); + $statement=WT_DB::prepare("SELECT o_gedcom FROM `##other` WHERE o_id=? AND o_file=? AND o_type='NOTE'"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } // The 'name' of a note record is the first line. This can be diff --git a/library/WT/Query/Media.php b/library/WT/Query/Media.php index 91c1b4a91c..241513846d 100644 --- a/library/WT/Query/Media.php +++ b/library/WT/Query/Media.php @@ -67,7 +67,7 @@ class WT_Query_Media { public static function mediaList($folder, $subfolders, $sort, $filter) { // All files in the folder, plus external files $sql = - "SELECT 'OBJE' AS type, m_id AS xref, m_file AS ged_id, m_gedcom AS gedrec, m_titl, m_filename" . + "SELECT m_id AS xref, m_file AS gedcom_id, m_gedcom AS gedcom" . " FROM `##media`" . " WHERE m_file=?"; $args = array( @@ -114,11 +114,11 @@ class WT_Query_Media { throw new Exception('Bad argument (sort=', $sort, ') in WT_Query_Media::mediaList()'); } - $rows = WT_DB::prepare($sql)->execute($args)->fetchAll(PDO::FETCH_ASSOC); + $rows = WT_DB::prepare($sql)->execute($args)->fetchAll(); $list = array(); foreach ($rows as $row) { - $media = WT_Media::getInstance($row); - if ($media->canDisplayDetails()) { + $media = WT_Media::getInstance($row->xref, $row->gedcom_id, $row->gedcom); + if ($media->canShow()) { $list[] = $media; } } diff --git a/library/WT/Query/Name.php b/library/WT/Query/Name.php index 4e04895d48..01a674dbb1 100644 --- a/library/WT/Query/Name.php +++ b/library/WT/Query/Name.php @@ -387,7 +387,7 @@ class WT_Query_Name { // To search for names with no surnames, use $salpha="," public static function individuals($surn, $salpha, $galpha, $marnm, $fams, $ged_id) { $sql= - "SELECT 'INDI' AS type, i_id AS xref, i_file AS ged_id, i_gedcom AS gedrec, n_full ". + "SELECT i_id AS xref, i_file AS gedcom_id, i_gedcom AS gedcom, n_full ". "FROM `##individuals` ". "JOIN `##name` ON (n_id=i_id AND n_file=i_file) ". ($fams ? "JOIN `##link` ON (n_id=l_from AND n_file=l_file AND l_type='FAMS') " : ""). @@ -413,12 +413,12 @@ class WT_Query_Name { $sql.=" ORDER BY CASE n_surn WHEN '@N.N.' THEN 1 ELSE 0 END, n_surn COLLATE '".WT_I18N::$collation."', CASE n_givn WHEN '@P.N.' THEN 1 ELSE 0 END, n_givn COLLATE '".WT_I18N::$collation."'"; $list=array(); - $rows=WT_DB::prepare($sql)->fetchAll(PDO::FETCH_ASSOC); + $rows=WT_DB::prepare($sql)->fetchAll(); foreach ($rows as $row) { - $person=WT_Person::getInstance($row); + $person=WT_Individual::getInstance($row->xref, $row->gedcom_id, $row->gedcom); // The name from the database may be private - check the filtered list... foreach ($person->getAllNames() as $n=>$name) { - if ($name['fullNN']==$row['n_full']) { + if ($name['fullNN']==$row->n_full) { $person->setPrimaryName($n); // We need to clone $person, as we may have multiple references to the // same person in this list, and the "primary name" would otherwise diff --git a/library/WT/Report/Base.php b/library/WT/Report/Base.php index 30e3cd9828..e39b81b338 100644 --- a/library/WT/Report/Base.php +++ b/library/WT/Report/Base.php @@ -458,7 +458,7 @@ class Element { $t = str_replace(array("<br>", " "), array("\n", " "), $t); if (!WT_RNEW) { $t = strip_tags($t); - $t = unhtmlentities($t); + $t = htmlspecialchars_decode($t); } $this->text .= $t; @@ -975,7 +975,7 @@ class Footnote extends Element { $t = str_replace(array("<br>", " "), array("\n", " "), $t); if (!WT_RNEW) { $t = strip_tags($t); - $t = unhtmlentities($t); + $t = htmlspecialchars_decode($t); } $this->text .= $t; return 0; @@ -1669,7 +1669,7 @@ function GedcomSHandler($attrs) { $newgedrec = ""; if (count($tags)<2) { $tmp=WT_GedcomRecord::getInstance($attrs['id']); - $newgedrec=$tmp ? $tmp->getGedcomRecord() : ''; + $newgedrec=$tmp ? $tmp->getGedcom() : ''; } if (empty($newgedrec)) { $tgedrec = $gedrec; @@ -1680,14 +1680,14 @@ function GedcomSHandler($attrs) { $newgedrec = $vars[$match[1]]['gedcom']; } else { $tmp=WT_GedcomRecord::getInstance($match[1]); - $newgedrec=$tmp ? $tmp->getGedcomRecord() : ''; + $newgedrec=$tmp ? $tmp->getGedcom() : ''; } } else { if (preg_match("/@(.+)/", $tag, $match)) { $gmatch = array(); if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) { $tmp=WT_GedcomRecord::getInstance($gmatch[1]); - $newgedrec=$tmp ? $tmp->getGedcomRecord() : ''; + $newgedrec=$tmp ? $tmp->getGedcom() : ''; $tgedrec = $newgedrec; } else { $newgedrec = ""; @@ -1942,7 +1942,7 @@ function GetPersonNameSHandler($attrs) { if (is_null($record)) { return; } - if (!$record->canDisplayName()) { + if (!$record->canShowName()) { $currentElement->addText(WT_I18N::translate('Private')); } else { $name = $record->getFullName(); @@ -2189,12 +2189,7 @@ function RepeatTagEHandler() { // Save original values array_push($parserStack, $parser); $oldgedrec = $gedrec; - // PHP 5.2.3 has a bug with foreach (), so don't use that here, while 5.2.3 is on the market - // while () has the fastest execution speed - $count = count($repeats); - $i = 0; - while ($i < $count) { - $gedrec = $repeats[$i]; + foreach ($repeats as $gedrec) { //-- start the sax parser $repeat_parser = xml_parser_create(); $parser = $repeat_parser; @@ -2211,7 +2206,6 @@ function RepeatTagEHandler() { exit; } xml_parser_free($repeat_parser); - $i++; } // Restore original values $gedrec = $oldgedrec; @@ -2334,28 +2328,21 @@ function FactsSHandler($attrs) { $tag = $vars[$match[1]]['id']; } + $record = WT_GedcomRecord::getInstance($id); if (empty($attrs['diff']) && !empty($id)) { - $record = WT_GedcomRecord::getInstance($id); $facts = $record->getFacts(); - if (!is_array($facts)) { - $facts = array($facts); - } sort_facts($facts); $repeats = array(); $nonfacts=explode(',', $tag); foreach ($facts as $event) { if (!in_array($event->getTag(), $nonfacts)) { - $repeats[]=$event->getGedComRecord(); + $repeats[]=$event->getGedcom(); } } } else { - $record = new WT_GedcomRecord($gedrec); - $oldrecord = WT_GedcomRecord::getInstance($record->getXref()); - $oldrecord->diffMerge($record); - $facts = $oldrecord->getFacts(); - foreach ($facts as $fact) { - if ($fact->getIsNew() && $fact->getTag()<>'CHAN') { - $repeats[]=$fact->getGedcomRecord(); + foreach ($record->getFacts as $fact) { + if ($fact->isNew() && $fact->getTag()<>'CHAN') { + $repeats[]=$fact->getGedcom(); } } } @@ -2604,7 +2591,7 @@ function FootnoteSHandler($attrs) { $id = $match[2]; } $record=WT_GedcomRecord::GetInstance($id); - if ($record && $record->canDisplayDetails()) { + if ($record && $record->canShow()) { array_push($printDataStack, $printData); $printData = true; $style = ""; @@ -2665,7 +2652,7 @@ function AgeAtDeathSHandler() { $id = ""; $match = array(); if (preg_match("/0 @(.+)@/", $gedrec, $match)) { - $person=WT_Person::getInstance($match[1]); + $person=WT_Individual::getInstance($match[1]); // Recorded age $fact_age=get_gedcom_value('AGE', 2, $gedrec); if ($fact_age=='') { @@ -2776,11 +2763,11 @@ function HighlightedImageSHandler($attrs) { if (!empty($attrs['width'])) $width = (int)$attrs['width']; if (!empty($attrs['height'])) $height = (int)$attrs['height']; - $person=WT_Person::getInstance($id); + $person=WT_Individual::getInstance($id); $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->canDisplayDetails() && $mediaobject->fileExists('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) and ($height==0)) { $perc = $width / $attributes['adjW']; $height= round($attributes['adjH']*$perc); @@ -2848,7 +2835,7 @@ function ImageSHandler($attrs) { if (preg_match("/\d OBJE @(.+)@/", $gedrec, $match)) { $mediaobject=WT_Media::getInstance($match[1], WT_GED_ID); $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->canDisplayDetails() && $mediaobject->fileExists('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) and ($height==0)) { $perc = $width / $attributes['adjW']; $height= round($attributes['adjH']*$perc); @@ -2972,7 +2959,7 @@ function ListSHandler($attrs) { switch ($listname) { case "pending": $rows=WT_DB::prepare( - "SELECT CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom". + "SELECT xref, gedcom_id, 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)". @@ -2983,7 +2970,7 @@ function ListSHandler($attrs) { )->execute(array(WT_GED_ID))->fetchAll(); $list=array(); foreach ($rows as $row) { - $list[]=new WT_GedcomRecord($row->gedcom); + $list[] = WT_GedcomRecord::getInstance($row->xref, $row->gedcom_id, $row->gedcom); } break; case "individual": @@ -2995,7 +2982,7 @@ function ListSHandler($attrs) { foreach ($attrs as $attr=>$value) { if ((strpos($attr, "filter")===0) && $value) { // Substitute global vars - $value=preg_replace('/\$(\w+)/e', '$vars["\\1"]["id"]', $value); + $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={$sql_col_prefix}file AND {$attr}.d_gid={$sql_col_prefix}id)"; @@ -3171,7 +3158,7 @@ function ListSHandler($attrs) { if ($filters) { foreach ($list as $key=>$record) { foreach ($filters as $filter) { - if (!preg_match("/".$filter."/i", $record->getGedcomRecord())) { + if (!preg_match("/".$filter."/i", $record->getGedcom())) { unset($list[$key]); break; } @@ -3182,7 +3169,7 @@ function ListSHandler($attrs) { $mylist = array(); foreach ($list as $indi) { $key=$indi->getXref(); - $grec=$indi->getGedcomRecord(); + $grec=$indi->getGedcom(); $keep = true; foreach ($filters2 as $filter) { if ($keep) { @@ -3247,10 +3234,10 @@ function ListSHandler($attrs) { uasort($list, array("WT_GedcomRecord", "CompareChanDate")); break; case "BIRT:DATE": - uasort($list, array("WT_Person", "CompareBirtDate")); + uasort($list, array("WT_Individual", "CompareBirtDate")); break; case "DEAT:DATE": - uasort($list, array("WT_Person", "CompareDeatDate")); + uasort($list, array("WT_Individual", "CompareDeatDate")); break; case "MARR:DATE": uasort($list, array("WT_Family", "CompareMarrDate")); @@ -3315,8 +3302,8 @@ function ListEHandler() { $list_total = count($list); $list_private = 0; foreach ($list as $record) { - if ($record->canDisplayDetails()) { - $gedrec = $record->getGedcomRecord(); + if ($record->canShow()) { + $gedrec = $record->getGedcom(); //-- start the sax parser $repeat_parser = xml_parser_create(); $parser = $repeat_parser; @@ -3411,7 +3398,7 @@ function RelativesSHandler($attrs) { } $list = array(); - $person = WT_Person::getInstance($id); + $person = WT_Individual::getInstance($id); if (!empty($person)) { $list[$id] = $person; switch ($group) { @@ -3558,7 +3545,7 @@ function RelativesEHandler() { continue; // key can be something like "empty7" } $tmp=WT_GedcomRecord::getInstance($key); - $gedrec = $tmp->getGedcomRecord(); + $gedrec = $tmp->getGedcom(); //-- start the sax parser $repeat_parser = xml_parser_create(); $parser = $repeat_parser; diff --git a/library/WT/Repository.php b/library/WT/Repository.php index c104238a68..e1e6fefd17 100644 --- a/library/WT/Repository.php +++ b/library/WT/Repository.php @@ -30,19 +30,18 @@ if (!defined('WT_WEBTREES')) { class WT_Repository extends WT_GedcomRecord { const RECORD_TYPE = 'REPO'; + const SQL_FETCH = "SELECT o_gedcom FROM `##other` WHERE o_id=? AND o_file=?"; const URL_PREFIX = 'repo.php?rid='; // Fetch the record from the database - protected static function fetchGedcomRecord($xref, $ged_id) { + protected static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; if ($statement===null) { - $statement=WT_DB::prepare( - "SELECT o_type AS type, o_id AS xref, o_file AS ged_id, o_gedcom AS gedrec ". - "FROM `##other` WHERE o_id=? AND o_file=?" - ); + $statement=WT_DB::prepare("SELECT o_gedcom FROM `##other` WHERE o_id=? AND o_file=?"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } // Generate a private version of this record diff --git a/library/WT/Source.php b/library/WT/Source.php index 3664e4af69..67c42c7793 100644 --- a/library/WT/Source.php +++ b/library/WT/Source.php @@ -30,21 +30,22 @@ if (!defined('WT_WEBTREES')) { class WT_Source extends WT_GedcomRecord { const RECORD_TYPE = 'SOUR'; + const SQL_FETCH = "SELECT s_gedcom FROM `##sources` WHERE s_id=? AND s_file=?"; const URL_PREFIX = 'source.php?sid='; // Implement source-specific privacy logic - protected function _canDisplayDetailsByType($access_level) { + protected function _canShowByType($access_level) { // Hide sources if they are attached to private repositories ... - preg_match_all('/\n1 REPO @(.+)@/', $this->_gedrec, $matches); + preg_match_all('/\n1 REPO @(.+)@/', $this->gedcom, $matches); foreach ($matches[1] as $match) { $repo=WT_Repository::getInstance($match); - if ($repo && !$repo->canDisplayDetails($access_level)) { + if ($repo && !$repo->canShow($access_level)) { return false; } } // ... otherwise apply default behaviour - return parent::_canDisplayDetailsByType($access_level); + return parent::_canShowByType($access_level); } // Generate a private version of this record @@ -53,20 +54,18 @@ class WT_Source extends WT_GedcomRecord { } // Fetch the record from the database - protected static function fetchGedcomRecord($xref, $ged_id) { + protected static function fetchGedcomRecord($xref, $gedcom_id) { static $statement=null; if ($statement===null) { - $statement=WT_DB::prepare( - "SELECT 'SOUR' AS type, s_id AS xref, s_file AS ged_id, s_gedcom AS gedrec ". - "FROM `##sources` WHERE s_id=? AND s_file=?" - ); + $statement=WT_DB::prepare("SELECT s_gedcom FROM `##sources` WHERE s_id=? AND s_file=?"); } - return $statement->execute(array($xref, $ged_id))->fetchOneRow(PDO::FETCH_ASSOC); + + return $statement->execute(array($xref, $gedcom_id))->fetchOne(); } public function getAuth() { - return get_gedcom_value('AUTH', 1, $this->getGedcomRecord()); + return get_gedcom_value('AUTH', 1, $this->getGedcom()); } // Get an array of structures containing all the names in the record diff --git a/library/WT/Stats.php b/library/WT/Stats.php index 3f538e1651..43bae5f13d 100644 --- a/library/WT/Stats.php +++ b/library/WT/Stats.php @@ -254,7 +254,7 @@ class WT_Stats { } function gedcomRootID() { - $root = WT_Person::getInstance(get_gedcom_setting(WT_GED_ID, 'PEDIGREE_ROOT_ID')); + $root = WT_Individual::getInstance(get_gedcom_setting(WT_GED_ID, 'PEDIGREE_ROOT_ID')); $root = substr($root, 0, stripos($root, "@") ); return $root; } @@ -842,7 +842,7 @@ class WT_Stats { switch($type) { default: case 'full': - if ($record->canDisplayDetails()) { + if ($record->canShow()) { $result=$record->format_list('span', false, $record->getFullName()); } else { $result=WT_I18N::translate('This information is private and cannot be shown.'); @@ -996,7 +996,7 @@ class WT_Stats { $surn_countries=array(); $indis = WT_Query_Name::individuals(utf8_strtoupper($surname), '', '', false, false, WT_GED_ID); foreach ($indis as $person) { - if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcomRecord(), $matches)) { + if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcom(), $matches)) { // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. foreach ($matches[1] as $country) { $country=trim($country); @@ -1377,11 +1377,11 @@ class WT_Stats { ); if (!isset($rows[0])) {return '';} $row = $rows[0]; - $person=WT_Person::getInstance($row['id']); + $person=WT_Individual::getInstance($row['id']); switch($type) { default: case 'full': - if ($person->canDisplayName()) { + if ($person->canShowName()) { $result=$person->format_list('span', false, $person->getFullName()); } else { $result= WT_I18N::translate('This information is private and cannot be shown.'); @@ -1435,7 +1435,7 @@ class WT_Stats { if (!isset($rows[0])) {return '';} $top10 = array(); foreach ($rows as $row) { - $person = WT_Person::getInstance($row['deathdate']); + $person = WT_Individual::getInstance($row['deathdate']); $age = $row['age']; if ((int)($age/365.25)>0) { $age = (int)($age/365.25).'y'; @@ -1445,7 +1445,7 @@ class WT_Stats { $age = $age.'d'; } $age = get_age_at_event($age, true); - if ($person->canDisplayDetails()) { + if ($person->canShow()) { if ($type == 'list') { $top10[]="<li><a href=\"".$person->getHtmlUrl()."\">".$person->getFullName()."</a> (".$age.")"."</li>"; } else { @@ -1502,7 +1502,7 @@ class WT_Stats { if (!isset($rows)) {return 0;} $top10 = array(); foreach ($rows as $row) { - $person=WT_Person::getInstance($row['id']); + $person=WT_Individual::getInstance($row['id']); $age = (WT_CLIENT_JD-$row['age']); if ((int)($age/365.25)>0) { $age = (int)($age/365.25).'y'; @@ -1776,7 +1776,7 @@ class WT_Stats { switch($type) { default: case 'full': - if ($record->canDisplayDetails()) { + if ($record->canShow()) { $result=$record->format_list('span', false, $record->getFullName()); } else { $result=WT_I18N::translate('This information is private and cannot be shown.'); @@ -1871,11 +1871,11 @@ class WT_Stats { if (!isset($rows[0])) {return '';} $row=$rows[0]; if (isset($row['famid'])) $family=WT_Family::getInstance($row['famid']); - if (isset($row['i_id'])) $person=WT_Person::getInstance($row['i_id']); + if (isset($row['i_id'])) $person=WT_Individual::getInstance($row['i_id']); switch($type) { default: case 'full': - if ($family->canDisplayDetails()) { + if ($family->canShow()) { $result=$family->format_list('span', false, $person->getFullName()); } else { $result=WT_I18N::translate('This information is private and cannot be shown.'); @@ -1990,7 +1990,7 @@ class WT_Stats { $husb = $family->getHusband(); $wife = $family->getWife(); if (($husb->getAllDeathDates() && $wife->getAllDeathDates()) || !$husb->isDead() || !$wife->isDead()) { - if ($family->canDisplayDetails()) { + if ($family->canShow()) { if ($type == 'list') { $top10[] = "<li><a href=\"".$family->getHtmlUrl()."\">".$family->getFullName()."</a> (".$age.")"."</li>"; } else { @@ -2054,7 +2054,7 @@ class WT_Stats { $age = $age.'d'; } $age = get_age_at_event($age, true); - if ($family->canDisplayDetails()) { + if ($family->canShow()) { if ($type == 'list') { $top10[] = "<li><a href=\"".$family->getHtmlUrl()."\">".$family->getFullName()."</a> (".$age.")"."</li>"; } else { @@ -2102,11 +2102,11 @@ class WT_Stats { ); if (!isset($rows[0])) {return '';} $row=$rows[0]; - if (isset($row['id'])) $person=WT_Person::getInstance($row['id']); + if (isset($row['id'])) $person=WT_Individual::getInstance($row['id']); switch($type) { default: case 'full': - if ($person->canDisplayDetails()) { + if ($person->canShow()) { $result=$person->format_list('span', false, $person->getFullName()); } else { $result=WT_I18N::translate('This information is private and cannot be shown.'); @@ -2523,7 +2523,7 @@ class WT_Stats { switch($type) { default: case 'full': - if ($family->canDisplayDetails()) { + if ($family->canShow()) { $result=$family->format_list('span', false, $family->getFullName()); } else { $result = WT_I18N::translate('This information is private and cannot be shown.'); @@ -2556,7 +2556,7 @@ class WT_Stats { $top10 = array(); for ($c = 0; $c < $total; $c++) { $family=WT_Family::getInstance($rows[$c]['id']); - if ($family->canDisplayDetails()) { + if ($family->canShow()) { if ($type == 'list') { $top10[]= '<li><a href="'.$family->getHtmlUrl().'">'.$family->getFullName().'</a> - '. @@ -2618,10 +2618,10 @@ class WT_Stats { if ($one) $dist = array(); foreach ($rows as $fam) { $family = WT_Family::getInstance($fam['family']); - $child1 = WT_Person::getInstance($fam['ch1']); - $child2 = WT_Person::getInstance($fam['ch2']); + $child1 = WT_Individual::getInstance($fam['ch1']); + $child2 = WT_Individual::getInstance($fam['ch2']); if ($type == 'name') { - if ($child1->canDisplayDetails() && $child2->canDisplayDetails()) { + if ($child1->canShow() && $child2->canShow()) { $return = '<a href="'.$child2->getHtmlUrl().'">'.$child2->getFullName().'</a> '; $return .= WT_I18N::translate('and').' '; $return .= '<a href="'.$child1->getHtmlUrl().'">'.$child1->getFullName().'</a>'; @@ -2645,7 +2645,7 @@ class WT_Stats { } if ($type == 'list') { if ($one && !in_array($fam['family'], $dist)) { - if ($child1->canDisplayDetails() && $child2->canDisplayDetails()) { + if ($child1->canShow() && $child2->canShow()) { $return = "<li>"; $return .= "<a href=\"".$child2->getHtmlUrl()."\">".$child2->getFullName()."</a> "; $return .= WT_I18N::translate('and')." "; @@ -2656,7 +2656,7 @@ class WT_Stats { $top10[] = $return; $dist[] = $fam['family']; } - } else if (!$one && $child1->canDisplayDetails() && $child2->canDisplayDetails()) { + } else if (!$one && $child1->canShow() && $child2->canShow()) { $return = "<li>"; $return .= "<a href=\"".$child2->getHtmlUrl()."\">".$child2->getFullName()."</a> "; $return .= WT_I18N::translate('and')." "; @@ -2667,7 +2667,7 @@ class WT_Stats { $top10[] = $return; } } else { - if ($child1->canDisplayDetails() && $child2->canDisplayDetails()) { + if ($child1->canShow() && $child2->canShow()) { $return = $child2->format_list('span', false, $child2->getFullName()); $return .= "<br>".WT_I18N::translate('and')."<br>"; $return .= $child1->format_list('span', false, $child1->getFullName()); @@ -2830,14 +2830,14 @@ class WT_Stats { $chl = array(); foreach ($rows as $row) { $family=WT_Family::getInstance($row['id']); - if ($family->canDisplayDetails()) { + if ($family->canShow()) { if ($tot==0) { $per = 0; } else { $per = round(100 * $row['tot'] / $tot, 0); } $chd .= self::_array_to_extended_encoding(array($per)); - $chl[] = strip_tags(unhtmlentities($family->getFullName())).' - '.WT_I18N::number($row['tot']); + $chl[] = htmlspecialchars_decode(strip_tags($family->getFullName())).' - '.WT_I18N::number($row['tot']); } } $chl = rawurlencode(join('|', $chl)); @@ -2968,7 +2968,7 @@ class WT_Stats { $top10 = array(); foreach ($rows as $row) { $family=WT_Family::getInstance($row['family']); - if ($family->canDisplayDetails()) { + if ($family->canShow()) { if ($type == 'list') { $top10[] = "<li><a href=\"".$family->getHtmlUrl()."\">".$family->getFullName()."</a></li>"; } else { @@ -3085,7 +3085,7 @@ class WT_Stats { $top10 = array(); foreach ($rows as $row) { $family=WT_Family::getInstance($row['id']); - if ($family->canDisplayDetails()) { + if ($family->canShow()) { if ($type == 'list') { $top10[]= '<li><a href="'.$family->getHtmlUrl().'">'.$family->getFullName().'</a> - '. diff --git a/library/phpmailer/class.phpmailer.php b/library/phpmailer/class.phpmailer.php index 3c4f95bcd4..bfb8ecad01 100644 --- a/library/phpmailer/class.phpmailer.php +++ b/library/phpmailer/class.phpmailer.php @@ -2,15 +2,16 @@ /*~ class.phpmailer.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.2 | -| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | +| Version: 5.2.6 | +| Site: https://github.com/PHPMailer/PHPMailer/ | | ------------------------------------------------------------------------- | -| Admin: Jim Jagielski (project admininistrator) | +| Admins: Marcus Bointon | +| Admins: Jim Jagielski | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | -| : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Marcus Bointon (coolbru) phpmailer@synchromedia.co.uk | | : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | -| Copyright (c) 2010-2011, Jim Jagielski. All Rights Reserved. | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -23,20 +24,25 @@ */ /** - * PHPMailer - PHP email transport class + * PHPMailer - PHP email creation and transport class * NOTE: Requires PHP version 5 or later * @package PHPMailer * @author Andy Prevost * @author Marcus Bointon * @author Jim Jagielski - * @copyright 2010 - 2011 Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost - * @version $Id$ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ -if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); +if (version_compare(PHP_VERSION, '5.0.0', '<') ) { + exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n"); +} +/** + * PHP email creation and transport class + * @package PHPMailer + */ class PHPMailer { ///////////////////////////////////////////////// @@ -53,7 +59,7 @@ class PHPMailer { * Sets the CharSet of the message. * @var string */ - public $CharSet = 'UTF-8'; + public $CharSet = 'iso-8859-1'; /** * Sets the Content-type of the message. @@ -87,35 +93,52 @@ class PHPMailer { public $FromName = 'Root User'; /** - * Sets the Sender email (Return-Path) of the message. If not empty, - * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. + * Sets the Sender email (Return-Path) of the message. + * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. * @var string */ public $Sender = ''; /** + * Sets the Return-Path of the message. If empty, it will + * be set to either From or Sender. + * @var string + */ + public $ReturnPath = ''; + + /** * Sets the Subject of the message. * @var string */ public $Subject = ''; /** - * Sets the Body of the message. This can be either an HTML or text body. - * If HTML then run IsHTML(true). + * An HTML or plain text message body. + * If HTML then call IsHTML(true). * @var string */ public $Body = ''; /** - * Sets the text-only body of the message. This automatically sets the - * email to multipart/alternative. This body can be read by mail - * clients that do not have HTML email capability such as mutt. Clients - * that can read HTML will view the normal Body. + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. * @var string */ public $AltBody = ''; /** + * An iCal message part body + * Only supported in simple alt or alt_inline message types + * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator + * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @link http://kigkonsult.se/iCalcreator/ + * @var string + */ + public $Ical = ''; + + /** * Stores the complete compiled MIME message body. * @var string * @access protected @@ -130,6 +153,13 @@ class PHPMailer { protected $MIMEHeader = ''; /** + * Stores the extra header list which CreateHeader() doesn't fold in + * @var string + * @access protected + */ + protected $mailHeader = ''; + + /** * Sets word wrapping on the body of the message to a given number of * characters. * @var int @@ -149,6 +179,13 @@ class PHPMailer { public $Sendmail = '/usr/sbin/sendmail'; /** + * Determine if mail() uses a fully sendmail compatible MTA that + * supports sendmail's "-oi -f" options + * @var boolean + */ + public $UseSendmailOptions = true; + + /** * Path to PHPMailer plugins. Useful if the SMTP class * is in a different directory than the PHP include path. * @var string @@ -176,12 +213,21 @@ class PHPMailer { */ public $MessageID = ''; + /** + * Sets the message Date to be used in the Date header. + * If empty, the current date will be added. + * @var string + */ + public $MessageDate = ''; + ///////////////////////////////////////////////// // PROPERTIES FOR SMTP ///////////////////////////////////////////////// /** - * Sets the SMTP hosts. All hosts must be separated by a + * Sets the SMTP hosts. + * + * All hosts must be separated by a * semicolon. You can also specify a different port * for each host by using this format: [hostname:port] * (e.g. "smtp1.example.com:25;smtp2.example.com"). @@ -203,8 +249,7 @@ class PHPMailer { public $Helo = ''; /** - * Sets connection prefix. - * Options are "", "ssl" or "tls" + * Sets connection prefix. Options are "", "ssl" or "tls" * @var string */ public $SMTPSecure = ''; @@ -228,6 +273,24 @@ class PHPMailer { public $Password = ''; /** + * Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM | CRAM-MD5 (default LOGIN) + * @var string + */ + public $AuthType = ''; + + /** + * Sets SMTP realm. + * @var string + */ + public $Realm = ''; + + /** + * Sets SMTP workstation. + * @var string + */ + public $Workstation = ''; + + /** * Sets the SMTP server timeout in seconds. * This function will not work with the win32 version. * @var int @@ -241,6 +304,13 @@ class PHPMailer { public $SMTPDebug = false; /** + * Sets the function/method to use for debugging output. + * Right now we only honor "echo" or "error_log" + * @var string + */ + public $Debugoutput = "echo"; + + /** * Prevents the SMTP connection from being closed after each mail * sending. If this is set to true then to close the connection * requires an explicit call to SmtpClose(). @@ -255,60 +325,89 @@ class PHPMailer { */ public $SingleTo = false; - /** + /** + * Should we generate VERP addresses when sending via SMTP? + * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @var bool + */ + public $do_verp = false; + + /** * If SingleTo is true, this provides the array to hold the email addresses * @var bool */ public $SingleToArray = array(); - /** - * Provides the ability to change the line ending + /** + * Should we allow sending messages with empty body? + * @var bool + */ + public $AllowEmpty = false; + + /** + * Provides the ability to change the generic line ending + * NOTE: The default remains '\n'. We force CRLF where we KNOW + * it must be used via self::CRLF * @var string */ public $LE = "\n"; - /** - * Used with DKIM DNS Resource Record + /** + * Used with DKIM Signing + * required parameter if DKIM is enabled + * + * domain selector example domainkey * @var string */ - public $DKIM_selector = 'phpmailer'; + public $DKIM_selector = ''; /** - * Used with DKIM DNS Resource Record - * optional, in format of email address 'you@yourdomain.com' + * Used with DKIM Signing + * required if DKIM is enabled, in format of email address 'you@yourdomain.com' typically used as the source of the email * @var string */ public $DKIM_identity = ''; /** - * Used with DKIM DNS Resource Record + * Used with DKIM Signing + * optional parameter if your private key requires a passphras * @var string */ public $DKIM_passphrase = ''; /** - * Used with DKIM DNS Resource Record - * optional, in format of email address 'you@yourdomain.com' + * Used with DKIM Singing + * required if DKIM is enabled, in format of email address 'domain.com' * @var string */ public $DKIM_domain = ''; /** - * Used with DKIM DNS Resource Record - * optional, in format of email address 'you@yourdomain.com' + * Used with DKIM Signing + * required if DKIM is enabled, path to private key file * @var string */ public $DKIM_private = ''; /** - * Callback Action function name - * the function that handles the result of the send email action. Parameters: + * Callback Action function name. + * The function that handles the result of the send email action. + * It is called out by Send() for each email sent. + * + * Value can be: + * - 'function_name' for function names + * - 'Class::Method' for static method calls + * - array($object, 'Method') for calling methods on $object + * See http://php.net/is_callable manual page for more details. + * + * Parameters: * bool $result result of the send action * string $to email address of the recipient * string $cc cc email addresses * string $bcc bcc email addresses * string $subject the subject * string $body the email body + * string $from email address of sender * @var string */ public $action_function = ''; //'callbackAction'; @@ -317,11 +416,11 @@ class PHPMailer { * Sets the PHPMailer Version number * @var string */ - public $Version = '5.2'; + public $Version = '5.2.6'; /** * What to use in the X-Mailer header - * @var string + * @var string NULL for default, whitespace for None, or actual string to use */ public $XMailer = ''; @@ -329,21 +428,85 @@ class PHPMailer { // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// - protected $smtp = NULL; + /** + * @var SMTP An instance of the SMTP sender class + * @access protected + */ + protected $smtp = null; + /** + * @var array An array of 'to' addresses + * @access protected + */ protected $to = array(); + /** + * @var array An array of 'cc' addresses + * @access protected + */ protected $cc = array(); + /** + * @var array An array of 'bcc' addresses + * @access protected + */ protected $bcc = array(); + /** + * @var array An array of reply-to name and address + * @access protected + */ protected $ReplyTo = array(); + /** + * @var array An array of all kinds of addresses: to, cc, bcc, replyto + * @access protected + */ protected $all_recipients = array(); + /** + * @var array An array of attachments + * @access protected + */ protected $attachment = array(); + /** + * @var array An array of custom headers + * @access protected + */ protected $CustomHeader = array(); + /** + * @var string The message's MIME type + * @access protected + */ protected $message_type = ''; + /** + * @var array An array of MIME boundary strings + * @access protected + */ protected $boundary = array(); + /** + * @var array An array of available languages + * @access protected + */ protected $language = array(); + /** + * @var integer The number of errors encountered + * @access protected + */ protected $error_count = 0; + /** + * @var string The filename of a DKIM certificate file + * @access protected + */ protected $sign_cert_file = ''; + /** + * @var string The filename of a DKIM key file + * @access protected + */ protected $sign_key_file = ''; + /** + * @var string The password of a DKIM key + * @access protected + */ protected $sign_key_pass = ''; + /** + * @var boolean Whether to throw exceptions for errors + * @access protected + */ protected $exceptions = false; ///////////////////////////////////////////////// @@ -353,12 +516,55 @@ class PHPMailer { const STOP_MESSAGE = 0; // message only, continue processing const STOP_CONTINUE = 1; // message?, likely ok to continue processing const STOP_CRITICAL = 2; // message, plus full stop, critical error reached + const CRLF = "\r\n"; // SMTP RFC specified EOL ///////////////////////////////////////////////// // METHODS, VARIABLES ///////////////////////////////////////////////// /** + * Calls actual mail() function, but in a safe_mode aware fashion + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do) + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string $params Params + * @access private + * @return bool + */ + private function mail_passthru($to, $subject, $body, $header, $params) { + if ( ini_get('safe_mode') || !($this->UseSendmailOptions) ) { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header); + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params); + } + return $rt; + } + + /** + * Outputs debugging info via user-defined method + * @param string $str + */ + protected function edebug($str) { + switch ($this->Debugoutput) { + case 'error_log': + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking display that's HTML-safe + echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet)."<br>\n"; + break; + case 'echo': + default: + //Just echoes exactly what was received + echo $str; + } + } + + /** * Constructor * @param boolean $exceptions Should we throw external exceptions? */ @@ -367,6 +573,15 @@ class PHPMailer { } /** + * Destructor + */ + public function __destruct() { + if ($this->Mailer == 'smtp') { //Close any open SMTP connection nicely + $this->SmtpClose(); + } + } + + /** * Sets message type to HTML. * @param bool $ishtml * @return void @@ -460,7 +675,7 @@ class PHPMailer { * @return boolean */ public function AddReplyTo($address, $name = '') { - return $this->AddAnAddress('ReplyTo', $address, $name); + return $this->AddAnAddress('Reply-To', $address, $name); } /** @@ -469,29 +684,34 @@ class PHPMailer { * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' * @param string $address The email address to send to * @param string $name + * @throws phpmailerException * @return boolean true on success, false if address already used or invalid in some way * @access protected */ protected function AddAnAddress($kind, $address, $name = '') { - if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { + if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { $this->SetError($this->Lang('Invalid recipient array').': '.$kind); if ($this->exceptions) { throw new phpmailerException('Invalid recipient array: ' . $kind); } - echo $this->Lang('Invalid recipient array').': '.$kind; + if ($this->SMTPDebug) { + $this->edebug($this->Lang('Invalid recipient array').': '.$kind); + } return false; } $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!self::ValidateAddress($address)) { + if (!$this->ValidateAddress($address)) { $this->SetError($this->Lang('invalid_address').': '. $address); if ($this->exceptions) { throw new phpmailerException($this->Lang('invalid_address').': '.$address); } - echo $this->Lang('invalid_address').': '.$address; + if ($this->SMTPDebug) { + $this->edebug($this->Lang('invalid_address').': '.$address); + } return false; } - if ($kind != 'ReplyTo') { + if ($kind != 'Reply-To') { if (!isset($this->all_recipients[strtolower($address)])) { array_push($this->$kind, array($address, $name)); $this->all_recipients[strtolower($address)] = true; @@ -506,29 +726,30 @@ class PHPMailer { return false; } -/** - * Set the From and FromName properties - * @param string $address - * @param string $name - * @return boolean - */ - public function SetFrom($address, $name = '', $auto = 1) { + /** + * Set the From and FromName properties + * @param string $address + * @param string $name + * @param boolean $auto Whether to also set the Sender address, defaults to true + * @throws phpmailerException + * @return boolean + */ + public function SetFrom($address, $name = '', $auto = true) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!self::ValidateAddress($address)) { + if (!$this->ValidateAddress($address)) { $this->SetError($this->Lang('invalid_address').': '. $address); if ($this->exceptions) { throw new phpmailerException($this->Lang('invalid_address').': '.$address); } - echo $this->Lang('invalid_address').': '.$address; + if ($this->SMTPDebug) { + $this->edebug($this->Lang('invalid_address').': '.$address); + } return false; } $this->From = $address; $this->FromName = $name; if ($auto) { - if (empty($this->ReplyTo)) { - $this->AddAnAddress('ReplyTo', $address, $name); - } if (empty($this->Sender)) { $this->Sender = $address; } @@ -538,25 +759,30 @@ class PHPMailer { /** * Check that a string looks roughly like an email address should - * Static so it can be used without instantiation - * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator - * Conforms approximately to RFC2822 - * @link http://www.hexillion.com/samples/#Regex Original pattern found here + * Static so it can be used without instantiation, public so people can overload + * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is + * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to + * not allow a@b type valid addresses :( + * @link http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice. * @param string $address The email address to check * @return boolean * @static * @access public */ public static function ValidateAddress($address) { - if (function_exists('filter_var')) { //Introduced in PHP 5.2 - if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { - return false; + if (defined('PCRE_VERSION')) { //Check this instead of extension_loaded so it works when that function is disabled + if (version_compare(PCRE_VERSION, '8.0') >= 0) { + return (boolean)preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); + } else { + //Fall back to an older regex that doesn't need a recent PCRE + return (boolean)preg_match('/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address); + } } else { - return true; + //No PCRE! Do something _very_ approximate! + //Check the address is 3 chars or longer and contains an @ that's not the first or last char + return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1); } - } else { - return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); - } } ///////////////////////////////////////////////// @@ -567,6 +793,7 @@ class PHPMailer { * Creates message and assigns Mailer. If the message is * not sent successfully then it returns false. Use the ErrorInfo * variable to view description of the error. + * @throws phpmailerException * @return bool */ public function Send() { @@ -574,6 +801,7 @@ class PHPMailer { if(!$this->PreSend()) return false; return $this->PostSend(); } catch (phpmailerException $e) { + $this->mailHeader = ''; $this->SetError($e->getMessage()); if ($this->exceptions) { throw $e; @@ -582,8 +810,14 @@ class PHPMailer { } } - protected function PreSend() { + /** + * Prep mail by constructing all message entities + * @throws phpmailerException + * @return bool + */ + public function PreSend() { try { + $this->mailHeader = ""; if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); } @@ -595,22 +829,33 @@ class PHPMailer { $this->error_count = 0; // reset errors $this->SetMessageType(); - //Refuse to send an empty message - if (empty($this->Body)) { + //Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty and empty($this->Body)) { throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); } $this->MIMEHeader = $this->CreateHeader(); $this->MIMEBody = $this->CreateBody(); + // To capture the complete message when using mail(), create + // an extra header list which CreateHeader() doesn't fold in + if ($this->Mailer == 'mail') { + if (count($this->to) > 0) { + $this->mailHeader .= $this->AddrAppend("To", $this->to); + } else { + $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); + } + $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); + } // digitally sign with DKIM if enabled - if ($this->DKIM_domain && $this->DKIM_private) { - $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); + if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) { + $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; } return true; + } catch (phpmailerException $e) { $this->SetError($e->getMessage()); if ($this->exceptions) { @@ -620,7 +865,13 @@ class PHPMailer { } } - protected function PostSend() { + /** + * Actual Email transport function + * Send the email via the selected mechanism + * @throws phpmailerException + * @return bool + */ + public function PostSend() { try { // Choose the mailer and send through it switch($this->Mailer) { @@ -628,35 +879,39 @@ class PHPMailer { return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); case 'smtp': return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->MailSend($this->MIMEHeader, $this->MIMEBody); default: return $this->MailSend($this->MIMEHeader, $this->MIMEBody); } - } catch (phpmailerException $e) { $this->SetError($e->getMessage()); if ($this->exceptions) { throw $e; } - echo $e->getMessage()."\n"; - return false; + if ($this->SMTPDebug) { + $this->edebug($e->getMessage()."\n"); + } } + return false; } /** * Sends mail using the $Sendmail program. * @param string $header The message headers * @param string $body The message body + * @throws phpmailerException * @access protected * @return bool */ protected function SendmailSend($header, $body) { if ($this->Sender != '') { - $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); } else { $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); } if ($this->SingleTo === true) { - foreach ($this->SingleToArray as $key => $val) { + foreach ($this->SingleToArray as $val) { if(!@$mail = popen($sendmail, 'w')) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -692,6 +947,7 @@ class PHPMailer { * Sends mail using the PHP mail() function. * @param string $header The message headers * @param string $body The message body + * @throws phpmailerException * @access protected * @return bool */ @@ -703,40 +959,27 @@ class PHPMailer { $to = implode(', ', $toArr); if (empty($this->Sender)) { - $params = "-oi -f %s"; + $params = " "; } else { - $params = sprintf("-oi -f %s", $this->Sender); + $params = sprintf("-f%s", $this->Sender); } if ($this->Sender != '' and !ini_get('safe_mode')) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $key => $val) { - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - } - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + $rt = false; + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $val) { + $rt = $this->mail_passthru($val, $this->Subject, $body, $header, $params); // implement call back function if it exists $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); } } else { - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $key => $val) { - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - } - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); - } + $rt = $this->mail_passthru($to, $this->Subject, $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); } if (isset($old_from)) { ini_set('sendmail_from', $old_from); @@ -752,6 +995,7 @@ class PHPMailer { * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. * @param string $header The message headers * @param string $body The message body + * @throws phpmailerException * @uses SMTP * @access protected * @return bool @@ -765,7 +1009,8 @@ class PHPMailer { } $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; if(!$this->smtp->Mail($smtp_from)) { - throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); + $this->SetError($this->Lang('from_failed') . $smtp_from . ' : ' .implode(',', $this->smtp->getError())); + throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); } // Attempt to send attach all recipients @@ -816,6 +1061,9 @@ class PHPMailer { } if($this->SMTPKeepAlive == true) { $this->smtp->Reset(); + } else { + $this->smtp->Quit(); + $this->smtp->Close(); } return true; } @@ -823,66 +1071,76 @@ class PHPMailer { /** * Initiates a connection to an SMTP server. * Returns false if the operation failed. + * @param array $options An array of options compatible with stream_context_create() * @uses SMTP * @access public + * @throws phpmailerException * @return bool */ - public function SmtpConnect() { + public function SmtpConnect($options = array()) { if(is_null($this->smtp)) { - $this->smtp = new SMTP(); + $this->smtp = new SMTP; + } + + //Already connected? + if ($this->smtp->Connected()) { + return true; } + $this->smtp->Timeout = $this->Timeout; $this->smtp->do_debug = $this->SMTPDebug; - $hosts = explode(';', $this->Host); + $this->smtp->Debugoutput = $this->Debugoutput; + $this->smtp->do_verp = $this->do_verp; $index = 0; - $connection = $this->smtp->Connected(); - - // Retry while there is no connection - try { - while($index < count($hosts) && !$connection) { - $hostinfo = array(); - if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } else { - $host = $hosts[$index]; - $port = $this->Port; - } - - $tls = ($this->SMTPSecure == 'tls'); - $ssl = ($this->SMTPSecure == 'ssl'); - - if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { + $tls = ($this->SMTPSecure == 'tls'); + $ssl = ($this->SMTPSecure == 'ssl'); + $hosts = explode(';', $this->Host); + $lastexception = null; - $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); + foreach ($hosts as $hostentry) { + $hostinfo = array(); + $host = $hostentry; + $port = $this->Port; + if (preg_match('/^(.+):([0-9]+)$/', $hostentry, $hostinfo)) { //If $hostentry contains 'address:port', override default + $host = $hostinfo[1]; + $port = $hostinfo[2]; + } + if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->ServerHostname(); + } $this->smtp->Hello($hello); if ($tls) { if (!$this->smtp->StartTLS()) { - throw new phpmailerException($this->Lang('tls')); + throw new phpmailerException($this->Lang('connect_host')); } - //We must resend HELO after tls negotiation $this->smtp->Hello($hello); } - - $connection = true; if ($this->SMTPAuth) { - if (!$this->smtp->Authenticate($this->Username, $this->Password)) { + if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation)) { throw new phpmailerException($this->Lang('authenticate')); } } - } - $index++; - if (!$connection) { - throw new phpmailerException($this->Lang('connect_host')); + return true; + } catch (phpmailerException $e) { + $lastexception = $e; + //We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->Quit(); } } - } catch (phpmailerException $e) { - $this->smtp->Reset(); - throw $e; } - return true; + //If we get here, all connection attempts have failed, so close connection hard + $this->smtp->Close(); + //As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and !is_null($lastexception)) { + throw $lastexception; + } + return false; } /** @@ -890,7 +1148,7 @@ class PHPMailer { * @return void */ public function SmtpClose() { - if(!is_null($this->smtp)) { + if ($this->smtp !== null) { if($this->smtp->Connected()) { $this->smtp->Quit(); $this->smtp->Close(); @@ -899,32 +1157,34 @@ class PHPMailer { } /** - * Sets the language for all class error messages. - * Returns false if it cannot load the language file. The default language is English. - * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory - * @access public - */ + * Sets the language for all class error messages. + * Returns false if it cannot load the language file. The default language is English. + * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") + * @param string $lang_path Path to the language file directory + * @return bool + * @access public + */ function SetLanguage($langcode = 'en', $lang_path = 'language/') { //Define full set of translatable strings $PHPMAILER_LANG = array( - 'provide_address' => 'You must provide at least one recipient email address.', + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: Data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address', 'mailer_not_supported' => ' mailer is not supported.', - 'execute' => 'Could not execute: ', - 'instantiate' => 'Could not instantiate mail function.', - 'authenticate' => 'SMTP Error: Could not authenticate.', - 'from_failed' => 'The following From address failed: ', - 'recipients_failed' => 'SMTP Error: The following recipients failed: ', - 'data_not_accepted' => 'SMTP Error: Data not accepted.', - 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', - 'file_access' => 'Could not access file: ', - 'file_open' => 'File Error: Could not open file: ', - 'encoding' => 'Unknown encoding: ', - 'signing' => 'Signing Error: ', - 'smtp_error' => 'SMTP server error: ', - 'empty_message' => 'Message body empty', - 'invalid_address' => 'Invalid address', - 'variable_set' => 'Cannot set or reset variable: ' + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_connect_failed' => 'SMTP Connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ' ); //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! $l = true; @@ -950,6 +1210,8 @@ class PHPMailer { /** * Creates recipient headers. * @access public + * @param string $type + * @param array $addr * @return string */ public function AddrAppend($type, $addr) { @@ -967,6 +1229,7 @@ class PHPMailer { /** * Formats an address correctly. * @access public + * @param string $addr * @return string */ public function AddrFormat($addr) { @@ -992,13 +1255,15 @@ class PHPMailer { // If utf-8 encoding is used, we will need to make sure we don't // split multibyte characters when we wrap $is_utf8 = (strtolower($this->CharSet) == "utf-8"); + $lelen = strlen($this->LE); + $crlflen = strlen(self::CRLF); $message = $this->FixEOL($message); - if (substr($message, -1) == $this->LE) { - $message = substr($message, 0, -1); + if (substr($message, -$lelen) == $this->LE) { + $message = substr($message, 0, -$lelen); } - $line = explode($this->LE, $message); + $line = explode($this->LE, $message); // Magic. We know FixEOL uses $LE $message = ''; for ($i = 0 ;$i < count($line); $i++) { $line_part = explode(' ', $line[$i]); @@ -1006,7 +1271,7 @@ class PHPMailer { for ($e = 0; $e<count($line_part); $e++) { $word = $line_part[$e]; if ($qp_mode and (strlen($word) > $length)) { - $space_left = $length - strlen($buf) - 1; + $space_left = $length - strlen($buf) - $crlflen; if ($e != 0) { if ($space_left > 20) { $len = $space_left; @@ -1020,13 +1285,16 @@ class PHPMailer { $part = substr($word, 0, $len); $word = substr($word, $len); $buf .= ' ' . $part; - $message .= $buf . sprintf("=%s", $this->LE); + $message .= $buf . sprintf("=%s", self::CRLF); } else { $message .= $buf . $soft_break; } $buf = ''; } while (strlen($word) > 0) { + if ($length <= 0) { + break; + } $len = $length; if ($is_utf8) { $len = $this->UTF8CharBoundary($word, $len); @@ -1039,7 +1307,7 @@ class PHPMailer { $word = substr($word, $len); if (strlen($word) > 0) { - $message .= $part . sprintf("=%s", $this->LE); + $message .= $part . sprintf("=%s", self::CRLF); } else { $buf = $part; } @@ -1054,7 +1322,7 @@ class PHPMailer { } } } - $message .= $buf . $this->LE; + $message .= $buf . self::CRLF; } return $message; @@ -1139,11 +1407,18 @@ class PHPMailer { $this->boundary[2] = 'b2_' . $uniq_id; $this->boundary[3] = 'b3_' . $uniq_id; - $result .= $this->HeaderLine('Date', self::RFCDate()); - if($this->Sender == '') { - $result .= $this->HeaderLine('Return-Path', trim($this->From)); + if ($this->MessageDate == '') { + $result .= $this->HeaderLine('Date', self::RFCDate()); + } else { + $result .= $this->HeaderLine('Date', $this->MessageDate); + } + + if ($this->ReturnPath) { + $result .= $this->HeaderLine('Return-Path', '<'.trim($this->ReturnPath).'>'); + } elseif ($this->Sender == '') { + $result .= $this->HeaderLine('Return-Path', '<'.trim($this->From).'>'); } else { - $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); + $result .= $this->HeaderLine('Return-Path', '<'.trim($this->Sender).'>'); } // To be created automatically by mail() @@ -1177,7 +1452,7 @@ class PHPMailer { } if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); + $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); } // mail() sets the subject itself @@ -1191,10 +1466,13 @@ class PHPMailer { $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); } $result .= $this->HeaderLine('X-Priority', $this->Priority); - if($this->XMailer) { - $result .= $this->HeaderLine('X-Mailer', $this->XMailer); + if ($this->XMailer == '') { + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer/)'); } else { - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->HeaderLine('X-Mailer', $myXmailer); + } } if($this->ConfirmReadingTo != '') { @@ -1221,76 +1499,88 @@ class PHPMailer { public function GetMailMIME() { $result = ''; switch($this->message_type) { - case 'plain': - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); - $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset="'.$this->CharSet.'"'); - break; case 'inline': $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); break; case 'attach': case 'inline_attach': case 'alt_attach': case 'alt_inline_attach': $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); break; case 'alt': case 'alt_inline': $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); break; + default: + // Catches case 'plain': and case '': + $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet); + break; + } + //RFC1341 part 5 says 7bit is assumed if not specified + if ($this->Encoding != '7bit') { + $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); } if($this->Mailer != 'mail') { - $result .= $this->LE.$this->LE; + $result .= $this->LE; } return $result; } /** + * Returns the MIME message (headers and body). Only really valid post PreSend(). + * @access public + * @return string + */ + public function GetSentMIMEMessage() { + return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; + } + + + /** * Assembles the message body. Returns an empty string on failure. * @access public + * @throws phpmailerException * @return string The assembled message body */ public function CreateBody() { $body = ''; if ($this->sign_key_file) { - $body .= $this->GetMailMIME(); + $body .= $this->GetMailMIME().$this->LE; } $this->SetWordWrap(); switch($this->message_type) { - case 'plain': - $body .= $this->EncodeString($this->Body, $this->Encoding); - break; case 'inline': $body .= $this->GetBoundary($this->boundary[1], '', '', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[1]); + $body .= $this->AttachAll('inline', $this->boundary[1]); break; case 'attach': $body .= $this->GetBoundary($this->boundary[1], '', '', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; case 'inline_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', '', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->AttachAll('inline', $this->boundary[2]); $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; case 'alt': $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); @@ -1299,27 +1589,32 @@ class PHPMailer { $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; + if(!empty($this->Ical)) { + $body .= $this->GetBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); + $body .= $this->EncodeString($this->Ical, $this->Encoding); + $body .= $this->LE.$this->LE; + } $body .= $this->EndBoundary($this->boundary[1]); break; case 'alt_inline': $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->AttachAll('inline', $this->boundary[2]); $body .= $this->LE; $body .= $this->EndBoundary($this->boundary[1]); break; case 'alt_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); @@ -1329,28 +1624,32 @@ class PHPMailer { $body .= $this->LE.$this->LE; $body .= $this->EndBoundary($this->boundary[2]); $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; case 'alt_inline_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->TextLine("--" . $this->boundary[2]); + $body .= $this->TextLine('--' . $this->boundary[2]); $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[3]); + $body .= $this->AttachAll('inline', $this->boundary[3]); $body .= $this->LE; $body .= $this->EndBoundary($this->boundary[2]); $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); + break; + default: + // catch case 'plain' and case '' + $body .= $this->EncodeString($this->Body, $this->Encoding); break; } @@ -1358,17 +1657,20 @@ class PHPMailer { $body = ''; } elseif ($this->sign_key_file) { try { - $file = tempnam('', 'mail'); + if (!defined('PKCS7_TEXT')) { + throw new phpmailerException($this->Lang('signing').' OpenSSL extension missing.'); + } + $file = tempnam(sys_get_temp_dir(), 'mail'); file_put_contents($file, $body); //TODO check this worked - $signed = tempnam("", "signed"); - if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { + $signed = tempnam(sys_get_temp_dir(), 'signed'); + if (@openssl_pkcs7_sign($file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null)) { @unlink($file); - @unlink($signed); $body = file_get_contents($signed); + @unlink($signed); } else { @unlink($file); @unlink($signed); - throw new phpmailerException($this->Lang("signing").openssl_error_string()); + throw new phpmailerException($this->Lang('signing').openssl_error_string()); } } catch (phpmailerException $e) { $body = ''; @@ -1377,13 +1679,16 @@ class PHPMailer { } } } - return $body; } /** * Returns the start of a message boundary. * @access protected + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding * @return string */ protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { @@ -1398,7 +1703,7 @@ class PHPMailer { $encoding = $this->Encoding; } $result .= $this->TextLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset=\"%s\"", $contentType, $charSet); + $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); $result .= $this->LE; $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); $result .= $this->LE; @@ -1409,6 +1714,7 @@ class PHPMailer { /** * Returns the end of a message boundary. * @access protected + * @param string $boundary * @return string */ protected function EndBoundary($boundary) { @@ -1430,8 +1736,10 @@ class PHPMailer { } /** - * Returns a formatted header line. + * Returns a formatted header line. * @access public + * @param string $name + * @param string $value * @return string */ public function HeaderLine($name, $value) { @@ -1441,6 +1749,7 @@ class PHPMailer { /** * Returns a formatted mail line. * @access public + * @param string $value * @return string */ public function TextLine($value) { @@ -1459,13 +1768,20 @@ class PHPMailer { * @param string $name Overrides the attachment name. * @param string $encoding File encoding (see $Encoding). * @param string $type File extension (MIME) type. + * @throws phpmailerException * @return bool */ - public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + public function AddAttachment($path, $name = '', $encoding = 'base64', $type = '') { try { if ( !@is_file($path) ) { throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); } + + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + $filename = basename($path); if ( $name == '' ) { $name = $filename; @@ -1487,10 +1803,10 @@ class PHPMailer { if ($this->exceptions) { throw $e; } - echo $e->getMessage()."\n"; - if ( $e->getCode() == self::STOP_CRITICAL ) { - return false; + if ($this->SMTPDebug) { + $this->edebug($e->getMessage()."\n"); } + return false; } return true; } @@ -1507,6 +1823,8 @@ class PHPMailer { * Attaches all fs, string, and binary attachments to the message. * Returns an empty string on failure. * @access protected + * @param string $disposition_type + * @param string $boundary * @return string */ protected function AttachAll($disposition_type, $boundary) { @@ -1520,6 +1838,8 @@ class PHPMailer { // CHECK IF IT IS A VALID DISPOSITION_FILTER if($attachment[6] == $disposition_type) { // Check for string attachment + $string = ''; + $path = ''; $bString = $attachment[5]; if ($bString) { $string = $attachment[0]; @@ -1547,7 +1867,13 @@ class PHPMailer { $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); } - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + //If a filename contains any of these chars, it should be quoted, but not otherwise: RFC2183 & RFC2045 5.1 + //Fixes a warning in IETF's msglint MIME checker + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + } else { + $mime[] = sprintf("Content-Disposition: %s; filename=%s%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + } // Encode as string attachment if($bString) { @@ -1576,6 +1902,7 @@ class PHPMailer { * Returns an empty string on failure. * @param string $path The full path to the file * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @throws phpmailerException * @see EncodeFile() * @access protected * @return string @@ -1585,19 +1912,22 @@ class PHPMailer { if (!is_readable($path)) { throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); } - if (function_exists('get_magic_quotes')) { - function get_magic_quotes() { - return false; + $magic_quotes = get_magic_quotes_runtime(); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime(0); + } else { + ini_set('magic_quotes_runtime', 0); } } - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - $magic_quotes = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - } $file_buffer = file_get_contents($path); $file_buffer = $this->EncodeString($file_buffer, $encoding); - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime($magic_quotes); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } else { + ini_set('magic_quotes_runtime', $magic_quotes); + } } return $file_buffer; } catch (Exception $e) { @@ -1643,6 +1973,8 @@ class PHPMailer { /** * Encode a header string to best (shortest) of Q, B, quoted or none. * @access public + * @param string $str + * @param string $position * @return string */ public function EncodeHeader($str, $position = 'text') { @@ -1670,18 +2002,18 @@ class PHPMailer { break; } - if ($x == 0) { + if ($x == 0) { //There are no chars that need encoding return ($str); } $maxlen = 75 - 7 - strlen($this->CharSet); // Try to select the encoding which should produce the shortest output - if (strlen($str)/3 < $x) { + if ($x > strlen($str)/3) { //More than a third of the content will need encoding, so B encoding will be most efficient $encoding = 'B'; if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { // Use a custom function which correctly encodes and wraps long // multibyte strings without breaking lines within a character - $encoded = $this->Base64EncodeWrapMB($str); + $encoded = $this->Base64EncodeWrapMB($str, "\n"); } else { $encoded = base64_encode($str); $maxlen -= $maxlen % 4; @@ -1691,7 +2023,7 @@ class PHPMailer { $encoding = 'Q'; $encoded = $this->EncodeQ($str, $position); $encoded = $this->WrapText($encoded, $maxlen, true); - $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); + $encoded = str_replace('='.self::CRLF, "\n", trim($encoded)); } $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); @@ -1720,12 +2052,16 @@ class PHPMailer { * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php * @access public * @param string $str multi-byte text to wrap encode + * @param string $lf string to use as linefeed/end-of-line * @return string */ - public function Base64EncodeWrapMB($str) { + public function Base64EncodeWrapMB($str, $lf=null) { $start = "=?".$this->CharSet."?B?"; $end = "?="; $encoded = ""; + if ($lf === null) { + $lf = $this->LE; + } $mb_length = mb_strlen($str, $this->CharSet); // Each line must have length <= 75, including $start and $end @@ -1746,94 +2082,43 @@ class PHPMailer { } while (strlen($chunk) > $length); - $encoded .= $chunk . $this->LE; + $encoded .= $chunk . $lf; } // Chomp the last linefeed - $encoded = substr($encoded, 0, -strlen($this->LE)); + $encoded = substr($encoded, 0, -strlen($lf)); return $encoded; } /** - * Encode string to quoted-printable. - * Only uses standard PHP, slow, but will always work - * @access public - * @param string $string the text to encode - * @param integer $line_max Number of chars allowed on a line before wrapping - * @return string - */ - public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { - $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); - $lines = preg_split('/(?:\r\n|\r|\n)/', $input); - $eol = "\r\n"; - $escape = '='; - $output = ''; - while( list(, $line) = each($lines) ) { - $linlen = strlen($line); - $newline = ''; - for($i = 0; $i < $linlen; $i++) { - $c = substr( $line, $i, 1 ); - $dec = ord( $c ); - if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E - $c = '=2E'; - } - if ( $dec == 32 ) { - if ( $i == ( $linlen - 1 ) ) { // convert space at eol only - $c = '=20'; - } else if ( $space_conv ) { - $c = '=20'; - } - } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required - $h2 = floor($dec/16); - $h1 = floor($dec%16); - $c = $escape.$hex[$h2].$hex[$h1]; - } - if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted - $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay - $newline = ''; - // check if newline first character will be point or not - if ( $dec == 46 ) { - $c = '=2E'; - } - } - $newline .= $c; - } // end of for - $output .= $newline.$eol; - } // end of while - return $output; - } - - /** - * Encode string to RFC2045 (6.7) quoted-printable format - * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version - * Also results in same content as you started with after decoding - * @see EncodeQPphp() - * @access public - * @param string $string the text to encode - * @param integer $line_max Number of chars allowed on a line before wrapping - * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function - * @return string - * @author Marcus Bointon - */ - public function EncodeQP($string, $line_max = 76, $space_conv = false) { + * Encode string to RFC2045 (6.7) quoted-printable format + * @access public + * @param string $string The text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 + */ + public function EncodeQP($string, $line_max = 76) { if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) return quoted_printable_encode($string); } - $filters = stream_get_filters(); - if (!in_array('convert.*', $filters)) { //Got convert stream filter? - return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation - } - $fp = fopen('php://temp/', 'r+'); - $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks - $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); - $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); - fputs($fp, $string); - rewind($fp); - $out = stream_get_contents($fp); - stream_filter_remove($s); - $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange - fclose($fp); - return $out; + //Fall back to a pure PHP implementation + $string = str_replace(array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string)); + $string = preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string); + return $string; + } + + /** + * Wrapper to preserve BC for old QP encoding function that was removed + * @see EncodeQP() + * @access public + * @param string $string + * @param integer $line_max + * @param bool $space_conv + * @return string + */ + public function EncodeQPphp($string, $line_max = 76, $space_conv = false) { + return $this->EncodeQP($string, $line_max); } /** @@ -1845,29 +2130,37 @@ class PHPMailer { * @return string */ public function EncodeQ($str, $position = 'text') { - // There should not be any EOL in the string - $encoded = preg_replace('/[\r\n]*/', '', $str); - + //There should not be any EOL in the string + $pattern = ''; + $encoded = str_replace(array("\r", "\n"), '', $str); switch (strtolower($position)) { case 'phrase': - $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + $pattern = '^A-Za-z0-9!*+\/ -'; break; + case 'comment': - $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + $pattern = '\(\)"'; + //note that we don't break here! + //for this reason we build the $pattern without including delimiters and [] + case 'text': default: - // Replace every high ascii, control =, ? and _ characters - //TODO using /e (equivalent to eval()) is probably not a good idea - $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', - "'='.sprintf('%02X', ord(stripslashes('\\1')))", $encoded); + //Replace every high ascii, control =, ? and _ characters + //We put \075 (=) as first value to make sure it's the first one in being converted, preventing double encode + $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern; break; } - // Replace every spaces to _ (more readable than =20) - $encoded = str_replace(' ', '_', $encoded); + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + + //Replace every spaces to _ (more readable than =20) + return str_replace(' ', '_', $encoded); +} - return $encoded; - } /** * Adds a string or binary attachment (non-filesystem) to the list. @@ -1879,7 +2172,11 @@ class PHPMailer { * @param string $type File extension (MIME) type. * @return void */ - public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { + public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = '') { + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($filename); + } // Append to $attachment array $this->attachment[] = array( 0 => $string, @@ -1894,25 +2191,27 @@ class PHPMailer { } /** - * Adds an embedded attachment. This can include images, sounds, and - * just about any other document. Make sure to set the $type to an - * image type. For JPEG images use "image/jpeg" and for GIF images - * use "image/gif". + * Add an embedded attachment from a file. + * This can include images, sounds, and just about any other document type. * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment. Use this to identify - * the Id for accessing the image in an HTML form. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. * @param string $name Overrides the attachment name. * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool + * @param string $type File MIME type. + * @return bool True on successfully adding an attachment */ - public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - + public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '') { if ( !@is_file($path) ) { $this->SetError($this->Lang('file_access') . $path); return false; } + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + $filename = basename($path); if ( $name == '' ) { $name = $filename; @@ -1929,22 +2228,41 @@ class PHPMailer { 6 => 'inline', 7 => $cid ); - return true; } - public function AddStringEmbeddedImage($string, $cid, $filename = '', $encoding = 'base64', $type = 'application/octet-stream') { + + /** + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * Be sure to set the $type to an image type for images: + * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. + * @param string $string The attachment binary data. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name + * @param string $encoding File encoding (see $Encoding). + * @param string $type MIME type. + * @return bool True on successfully adding an attachment + */ + public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = '') { + //If a MIME type is not specified, try to work it out from the name + if ($type == '') { + $type = self::filenameToType($name); + } + // Append to $attachment array $this->attachment[] = array( 0 => $string, - 1 => $filename, - 2 => basename($filename), + 1 => $name, + 2 => $name, 3 => $encoding, 4 => $type, 5 => true, // isStringAttachment 6 => 'inline', 7 => $cid ); + return true; } /** @@ -1961,6 +2279,10 @@ class PHPMailer { return false; } + /** + * Returns true if an attachment (non-inline) is present. + * @return bool + */ public function AttachmentExists() { foreach($this->attachment as $attachment) { if ($attachment[6] == 'attachment') { @@ -1970,8 +2292,12 @@ class PHPMailer { return false; } + /** + * Does this message have an alternative body set? + * @return bool + */ public function AlternativeExists() { - return strlen($this->AltBody)>0; + return !empty($this->AltBody); } ///////////////////////////////////////////////// @@ -2055,6 +2381,7 @@ class PHPMailer { /** * Adds the error message to the error container. * @access protected + * @param string $msg * @return void */ protected function SetError($msg) { @@ -2075,13 +2402,10 @@ class PHPMailer { * @static */ public static function RFCDate() { - $tz = date('Z'); - $tzs = ($tz < 0) ? '-' : '+'; - $tz = abs($tz); - $tz = (int)($tz/3600)*100 + ($tz%3600)/60; - $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); - - return $result; + //Set the time zone to whatever the default is to avoid 500 errors + //Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + return date('D, j M Y H:i:s O'); } /** @@ -2104,6 +2428,7 @@ class PHPMailer { /** * Returns a message in the appropriate language. * @access protected + * @param string $key * @return string */ protected function Lang($key) { @@ -2128,84 +2453,121 @@ class PHPMailer { } /** - * Changes every end of line from CR or LF to CRLF. + * Changes every end of line from CRLF, CR or LF to $this->LE. * @access public + * @param string $str String to FixEOL * @return string */ public function FixEOL($str) { - $str = str_replace("\r\n", "\n", $str); - $str = str_replace("\r", "\n", $str); - $str = str_replace("\n", $this->LE, $str); - return $str; + // condense down to \n + $nstr = str_replace(array("\r\n", "\r"), "\n", $str); + // Now convert LE as needed + if ($this->LE !== "\n") { + $nstr = str_replace("\n", $this->LE, $nstr); + } + return $nstr; } /** - * Adds a custom header. + * Adds a custom header. $name value can be overloaded to contain + * both header name and value (name:value) * @access public + * @param string $name custom header name + * @param string $value header value * @return void */ - public function AddCustomHeader($custom_header) { - $this->CustomHeader[] = explode(':', $custom_header, 2); + public function AddCustomHeader($name, $value=null) { + if ($value === null) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = array($name, $value); + } } /** - * Evaluates the message and returns modifications for inline images and backgrounds + * Creates a message from an HTML string, making modifications for inline images and backgrounds + * and creates a plain-text version by converting the HTML + * Overwrites any existing values in $this->Body and $this->AltBody * @access public - * @return $message + * @param string $message HTML message string + * @param string $basedir baseline directory for path + * @param bool $advanced Whether to use the advanced HTML to text converter + * @return string $message */ - public function MsgHTML($message, $basedir = '') { - preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); - if(isset($images[2])) { - foreach($images[2] as $i => $url) { + public function MsgHTML($message, $basedir = '', $advanced = false) { + preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); + if (isset($images[2])) { + foreach ($images[2] as $i => $url) { // do not change urls for absolute images (thanks to corvuscorax) if (!preg_match('#^[A-z]+://#', $url)) { $filename = basename($url); $directory = dirname($url); - ($directory == '.') ? $directory='': ''; - $cid = 'cid:' . md5($filename); - $ext = pathinfo($filename, PATHINFO_EXTENSION); - $mimeType = self::_mime_types($ext); - if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } - if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } - if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64', $mimeType) ) { - $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); + if ($directory == '.') { + $directory = ''; + } + $cid = md5($url).'@phpmailer.0'; //RFC2392 S 2 + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + $basedir .= '/'; + } + if (strlen($directory) > 1 && substr($directory, -1) != '/') { + $directory .= '/'; + } + if ($this->AddEmbeddedImage($basedir.$directory.$filename, $cid, $filename, 'base64', self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)))) { + $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"cid:".$cid."\"", $message); } } } } $this->IsHTML(true); - $this->Body = $message; - $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); - if (!empty($textMsg) && empty($this->AltBody)) { - $this->AltBody = html_entity_decode($textMsg); - } if (empty($this->AltBody)) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; } + //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better + $this->Body = $this->NormalizeBreaks($message); + $this->AltBody = $this->NormalizeBreaks($this->html2text($message, $advanced)); + return $this->Body; + } + + /** + * Convert an HTML string into a plain text version + * @param string $html The HTML text to convert + * @param bool $advanced Should this use the more complex html2text converter or just a simple one? + * @return string + */ + public function html2text($html, $advanced = false) { + if ($advanced) { + require_once 'extras/class.html2text.php'; + $h = new html2text($html); + return $h->get_text(); + } + return html_entity_decode(trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet); } /** * Gets the MIME type of the embedded or inline image - * @param string File extension + * @param string $ext File extension * @access public * @return string MIME type of ext * @static */ public static function _mime_types($ext = '') { $mimes = array( + 'xl' => 'application/excel', 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', - 'doc' => 'application/msword', 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', 'lha' => 'application/octet-stream', 'lzh' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'class' => 'application/octet-stream', 'psd' => 'application/octet-stream', - 'so' => 'application/octet-stream', 'sea' => 'application/octet-stream', - 'dll' => 'application/octet-stream', + 'so' => 'application/octet-stream', 'oda' => 'application/oda', 'pdf' => 'application/pdf', 'ai' => 'application/postscript', @@ -2223,9 +2585,9 @@ class PHPMailer { 'dxr' => 'application/x-director', 'dvi' => 'application/x-dvi', 'gtar' => 'application/x-gtar', - 'php' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', 'phtml' => 'application/x-httpd-php', 'phps' => 'application/x-httpd-php-source', 'js' => 'application/x-javascript', @@ -2233,69 +2595,131 @@ class PHPMailer { 'sit' => 'application/x-stuffit', 'tar' => 'application/x-tar', 'tgz' => 'application/x-tar', - 'xhtml' => 'application/xhtml+xml', 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', 'zip' => 'application/zip', 'mid' => 'audio/midi', 'midi' => 'audio/midi', - 'mpga' => 'audio/mpeg', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', 'rpm' => 'audio/x-pn-realaudio-plugin', 'ra' => 'audio/x-realaudio', - 'rv' => 'video/vnd.rn-realvideo', 'wav' => 'audio/x-wav', 'bmp' => 'image/bmp', 'gif' => 'image/gif', 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', 'png' => 'image/png', 'tiff' => 'image/tiff', 'tif' => 'image/tiff', + 'eml' => 'message/rfc822', 'css' => 'text/css', 'html' => 'text/html', 'htm' => 'text/html', 'shtml' => 'text/html', - 'txt' => 'text/plain', - 'text' => 'text/plain', 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', 'rtx' => 'text/richtext', 'rtf' => 'text/rtf', 'xml' => 'text/xml', 'xsl' => 'text/xml', 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', + 'mpg' => 'video/mpeg', 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', - 'word' => 'application/msword', - 'xl' => 'application/excel', - 'eml' => 'message/rfc822' + 'movie' => 'video/x-sgi-movie' ); return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; } /** - * Set (or reset) Class Objects (variables) - * - * Usage Example: - * $page->set('X-Priority', '3'); - * - * @access public - * @param string $name Parameter Name - * @param mixed $value Parameter Value - * NOTE: will not work with arrays, there are no arrays to set/reset - * @todo Should this not be using __set() magic function? - */ + * Try to map a file name to a MIME type, default to application/octet-stream + * @param string $filename A file name or full path, does not need to exist as a file + * @return string + * @static + */ + public static function filenameToType($filename) { + //In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if ($qpos !== false) { + $filename = substr($filename, 0, $qpos); + } + $pathinfo = self::mb_pathinfo($filename); + return self::_mime_types($pathinfo['extension']); + } + + /** + * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. + * Works similarly to the one in PHP >= 5.2.0 + * @link http://www.php.net/manual/en/function.pathinfo.php#107461 + * @param string $path A filename or path, does not need to exist as a file + * @param integer|string $options Either a PATHINFO_* constant, or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 + * @return string|array + * @static + */ + public static function mb_pathinfo($path, $options = null) { + $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); + $m = array(); + preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); + if(array_key_exists(1, $m)) { + $ret['dirname'] = $m[1]; + } + if(array_key_exists(2, $m)) { + $ret['basename'] = $m[2]; + } + if(array_key_exists(5, $m)) { + $ret['extension'] = $m[5]; + } + if(array_key_exists(3, $m)) { + $ret['filename'] = $m[3]; + } + switch($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + break; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + break; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + break; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + break; + default: + return $ret; + } + } + + /** + * Set (or reset) Class Objects (variables) + * + * Usage Example: + * $page->set('X-Priority', '3'); + * + * @access public + * @param string $name + * @param mixed $value + * NOTE: will not work with arrays, there are no arrays to set/reset + * @throws phpmailerException + * @return bool + * @todo Should this not be using __set() magic function? + */ public function set($name, $value = '') { try { if (isset($this->$name) ) { @@ -2315,20 +2739,33 @@ class PHPMailer { /** * Strips newlines to prevent header injection. * @access public - * @param string $str String + * @param string $str * @return string */ public function SecureHeader($str) { - $str = str_replace("\r", '', $str); - $str = str_replace("\n", '', $str); - return trim($str); + return trim(str_replace(array("\r", "\n"), '', $str)); } /** + * Normalize UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format + * Defaults to CRLF (for message bodies) and preserves consecutive breaks + * @param string $text + * @param string $breaktype What kind of line break to use, defaults to CRLF + * @return string + * @access public + * @static + */ + public static function NormalizeBreaks($text, $breaktype = "\r\n") { + return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); + } + + + /** * Set the private key file and password to sign the message. * * @access public - * @param string $key_filename Parameter File Name + * @param string $cert_filename + * @param string $key_filename * @param string $key_pass Password for private key */ public function Sign($cert_filename, $key_filename, $key_pass) { @@ -2341,11 +2778,10 @@ class PHPMailer { * Set the private key file and password to sign the message. * * @access public - * @param string $key_filename Parameter File Name - * @param string $key_pass Password for private key + * @param string $txt + * @return string */ public function DKIM_QP($txt) { - $tmp = ''; $line = ''; for ($i = 0; $i < strlen($txt); $i++) { $ord = ord($txt[$i]); @@ -2363,8 +2799,16 @@ class PHPMailer { * * @access public * @param string $s Header + * @throws phpmailerException + * @return string */ public function DKIM_Sign($s) { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new phpmailerException($this->Lang("signing").' OpenSSL extension missing.'); + } + return ''; + } $privKeyStr = file_get_contents($this->DKIM_private); if ($this->DKIM_passphrase != '') { $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); @@ -2374,6 +2818,7 @@ class PHPMailer { if (openssl_sign($s, $signature, $privKey)) { return base64_encode($signature); } + return ''; } /** @@ -2381,6 +2826,7 @@ class PHPMailer { * * @access public * @param string $s Header + * @return string */ public function DKIM_HeaderC($s) { $s = preg_replace("/\r\n\s+/", " ", $s); @@ -2400,6 +2846,7 @@ class PHPMailer { * * @access public * @param string $body Message Body + * @return string */ public function DKIM_BodyC($body) { if ($body == '') return "\r\n"; @@ -2420,6 +2867,7 @@ class PHPMailer { * @param string $headers_line Header lines * @param string $subject Subject * @param string $body Body + * @return string */ public function DKIM_Add($headers_line, $subject, $body) { $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms @@ -2428,11 +2876,22 @@ class PHPMailer { $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) $subject_header = "Subject: $subject"; $headers = explode($this->LE, $headers_line); + $from_header = ''; + $to_header = ''; + $current = ''; foreach($headers as $header) { if (strpos($header, 'From:') === 0) { $from_header = $header; + $current = 'from_header'; } elseif (strpos($header, 'To:') === 0) { $to_header = $header; + $current = 'to_header'; + } else { + if($current && strpos($header, ' =?') === 0){ + $current .= $header; + } else { + $current = ''; + } } } $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); @@ -2453,21 +2912,38 @@ class PHPMailer { "\tb="; $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); $signed = $this->DKIM_Sign($toSign); - return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n"; + return $dkimhdrs.$signed."\r\n"; } - protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body) { - if (!empty($this->action_function) && function_exists($this->action_function)) { - $params = array($isSent, $to, $cc, $bcc, $subject, $body); + /** + * Perform callback + * @param boolean $isSent + * @param string $to + * @param string $cc + * @param string $bcc + * @param string $subject + * @param string $body + * @param string $from + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) { + if (!empty($this->action_function) && is_callable($this->action_function)) { + $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); call_user_func_array($this->action_function, $params); } } } +/** + * Exception handler for PHPMailer + * @package PHPMailer + */ class phpmailerException extends Exception { + /** + * Prettify error message output + * @return string + */ public function errorMessage() { $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; return $errorMsg; } } -?> diff --git a/library/phpmailer/class.smtp.php b/library/phpmailer/class.smtp.php index 22d8e951d8..4b02f99915 100644 --- a/library/phpmailer/class.smtp.php +++ b/library/phpmailer/class.smtp.php @@ -2,15 +2,16 @@ /*~ class.smtp.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.2 | -| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | +| Version: 5.2.6 | +| Site: https://github.com/PHPMailer/PHPMailer/ | | ------------------------------------------------------------------------- | -| Admin: Jim Jagielski (project admininistrator) | +| Admins: Marcus Bointon | +| Admins: Jim Jagielski | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | -| : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Marcus Bointon (coolbru) phpmailer@synchromedia.co.uk | | : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | -| Copyright (c) 2010-2011, Jim Jagielski. All Rights Reserved. | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -30,17 +31,17 @@ * @author Marcus Bointon * @copyright 2004 - 2008 Andy Prevost * @author Jim Jagielski - * @copyright 2010 - 2011 Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) - * @version $Id$ */ /** - * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP - * commands except TURN which will always return a not implemented - * error. SMTP also provides some utility methods for sending mail - * to an SMTP server. - * original author: Chris Ryan + * PHP RFC821 SMTP client + * + * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error. + * SMTP also provides some utility methods for sending mail to an SMTP server. + * @author Chris Ryan + * @package PHPMailer */ class SMTP { @@ -51,16 +52,23 @@ class SMTP { public $SMTP_PORT = 25; /** - * SMTP reply line ending + * SMTP reply line ending (don't change) * @var string */ public $CRLF = "\r\n"; /** - * Sets whether debugging is turned on - * @var bool + * Debug output level; 0 for no output + * @var int + */ + public $do_debug = 0; + + /** + * Sets the function/method to use for debugging output. + * Right now we only honor 'echo', 'html' or 'error_log' + * @var string */ - public $do_debug; // the level of debug to perform + public $Debugoutput = 'echo'; /** * Sets VERP use on/off (default is off) @@ -69,23 +77,64 @@ class SMTP { public $do_verp = false; /** + * Sets the SMTP timeout value for reads, in seconds + * @var int + */ + public $Timeout = 15; + + /** + * Sets the SMTP timelimit value for reads, in seconds + * @var int + */ + public $Timelimit = 30; + + /** * Sets the SMTP PHPMailer Version number * @var string */ - public $Version = '5.2'; + public $Version = '5.2.6'; ///////////////////////////////////////////////// // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// - private $smtp_conn; // the socket to the server - private $error; // error if any on the last call - private $helo_rply; // the reply the server sent to us for HELO + /** + * @var resource The socket to the server + */ + protected $smtp_conn; + /** + * @var string Error message, if any, for the last call + */ + protected $error; + /** + * @var string The reply the server sent to us for HELO + */ + protected $helo_rply; + + /** + * Outputs debugging info via user-defined method + * @param string $str + */ + protected function edebug($str) { + switch ($this->Debugoutput) { + case 'error_log': + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking display that's HTML-safe + echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8')."<br>\n"; + break; + case 'echo': + default: + //Just echoes exactly what was received + echo $str; + } + } /** * Initialize the class so that the data is in a known state. * @access public - * @return void + * @return SMTP */ public function __construct() { $this->smtp_conn = 0; @@ -100,26 +149,25 @@ class SMTP { ///////////////////////////////////////////////// /** - * Connect to the server specified on the port specified. - * If the port is not specified use the default SMTP_PORT. - * If tval is specified then a connection will try and be - * established with the server for that number of seconds. - * If tval is not specified the default is 30 seconds to - * try on the connection. + * Connect to an SMTP server * * SMTP CODE SUCCESS: 220 * SMTP CODE FAILURE: 421 * @access public + * @param string $host SMTP server IP or host name + * @param int $port The port number to connect to, or use the default port if not specified + * @param int $timeout How long to wait for the connection to open + * @param array $options An array of options compatible with stream_context_create() * @return bool */ - public function Connect($host, $port = 0, $tval = 30) { - // set the error val to null so there is no confusion + public function Connect($host, $port = 0, $timeout = 30, $options = array()) { + // Clear errors to avoid confusion $this->error = null; - // make sure we are __not__ connected + // Make sure we are __not__ connected if($this->connected()) { - // already connected, generate error - $this->error = array("error" => "Already connected to a server"); + // Already connected, generate error + $this->error = array('error' => 'Already connected to a server'); return false; } @@ -127,33 +175,39 @@ class SMTP { $port = $this->SMTP_PORT; } - // connect to the smtp server - $this->smtp_conn = @fsockopen($host, // the host of the server - $port, // the port to use - $errno, // error number if any - $errstr, // error message if any - $tval); // give up after ? secs - // verify we connected properly + // Connect to the SMTP server + $errno = 0; + $errstr = ''; + $socket_context = stream_context_create($options); + //Need to suppress errors here as connection failures can be handled at a higher level + $this->smtp_conn = @stream_socket_client($host.":".$port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context); + + // Verify we connected properly if(empty($this->smtp_conn)) { - $this->error = array("error" => "Failed to connect to server", - "errno" => $errno, - "errstr" => $errstr); + $this->error = array('error' => 'Failed to connect to server', + 'errno' => $errno, + 'errstr' => $errstr); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ": $errstr ($errno)"); } return false; } // SMTP server can take longer to respond, give longer timeout for first read // Windows does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != "WIN") - socket_set_timeout($this->smtp_conn, $tval, 0); + if(substr(PHP_OS, 0, 3) != 'WIN') { + $max = ini_get('max_execution_time'); + if ($max != 0 && $timeout > $max) { // Don't bother if unlimited + @set_time_limit($timeout); + } + stream_set_timeout($this->smtp_conn, $timeout, 0); + } // get any announcement $announce = $this->get_lines(); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $announce); } return true; @@ -172,26 +226,26 @@ class SMTP { $this->error = null; # to avoid confusion if(!$this->connected()) { - $this->error = array("error" => "Called StartTLS() without being connected"); + $this->error = array('error' => 'Called StartTLS() without being connected'); return false; } - fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); + $this->client_send('STARTTLS' . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 220) { $this->error = - array("error" => "STARTTLS not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'STARTTLS not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -208,61 +262,237 @@ class SMTP { * Performs SMTP authentication. Must be run after running the * Hello() method. Returns true if successfully authenticated. * @access public + * @param string $username + * @param string $password + * @param string $authtype + * @param string $realm + * @param string $workstation * @return bool */ - public function Authenticate($username, $password) { - // Start authentication - fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') { + if (empty($authtype)) { + $authtype = 'LOGIN'; + } - $rply = $this->get_lines(); - $code = substr($rply,0,3); + switch ($authtype) { + case 'PLAIN': + // Start authentication + $this->client_send('AUTH PLAIN' . $this->CRLF); - if($code != 334) { - $this->error = - array("error" => "AUTH not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; - } - return false; - } + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); - // Send encoded username - fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + if($code != 334) { + $this->error = + array('error' => 'AUTH not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + // Send encoded username and password + $this->client_send(base64_encode("\0".$username."\0".$password) . $this->CRLF); - $rply = $this->get_lines(); - $code = substr($rply,0,3); + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); - if($code != 334) { - $this->error = - array("error" => "Username not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; - } - return false; + if($code != 235) { + $this->error = + array('error' => 'Authentication not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + break; + case 'LOGIN': + // Start authentication + $this->client_send('AUTH LOGIN' . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 334) { + $this->error = + array('error' => 'AUTH not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + + // Send encoded username + $this->client_send(base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 334) { + $this->error = + array('error' => 'Username not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + + // Send encoded password + $this->client_send(base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 235) { + $this->error = + array('error' => 'Password not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + break; + case 'NTLM': + /* + * ntlm_sasl_client.php + ** Bundled with Permission + ** + ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx + ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication + */ + require_once 'extras/ntlm_sasl_client.php'; + $temp = new stdClass(); + $ntlm_client = new ntlm_sasl_client_class; + if(! $ntlm_client->Initialize($temp)){//let's test if every function its available + $this->error = array('error' => $temp->error); + if($this->do_debug >= 1) { + $this->edebug('You need to enable some modules in your php.ini file: ' . $this->error['error']); + } + return false; + } + $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1 + + $this->client_send('AUTH NTLM ' . base64_encode($msg1) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 334) { + $this->error = + array('error' => 'AUTH not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + + $challenge = substr($rply, 3);//though 0 based, there is a white space after the 3 digit number....//msg2 + $challenge = base64_decode($challenge); + $ntlm_res = $ntlm_client->NTLMResponse(substr($challenge, 24, 8), $password); + $msg3 = $ntlm_client->TypeMsg3($ntlm_res, $username, $realm, $workstation);//msg3 + // Send encoded username + $this->client_send(base64_encode($msg3) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 235) { + $this->error = + array('error' => 'Could not authenticate', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + break; + case 'CRAM-MD5': + // Start authentication + $this->client_send('AUTH CRAM-MD5' . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 334) { + $this->error = + array('error' => 'AUTH not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + + // Get the challenge + $challenge = base64_decode(substr($rply, 4)); + + // Build the response + $response = $username . ' ' . $this->hmac($challenge, $password); + + // Send encoded credentials + $this->client_send(base64_encode($response) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply, 0, 3); + + if($code != 235) { + $this->error = + array('error' => 'Credentials not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); + if($this->do_debug >= 1) { + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); + } + return false; + } + break; } + return true; + } - // Send encoded password - fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + /** + * Works like hash_hmac('md5', $data, $key) in case that function is not available + * @access protected + * @param string $data + * @param string $key + * @return string + */ + protected function hmac($data, $key) { + if (function_exists('hash_hmac')) { + return hash_hmac('md5', $data, $key); + } - $rply = $this->get_lines(); - $code = substr($rply,0,3); + // The following borrowed from http://php.net/manual/en/function.mhash.php#27225 - if($code != 235) { - $this->error = - array("error" => "Password not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + // RFC 2104 HMAC implementation for php. + // Creates an md5 HMAC. + // Eliminates the need to install mhash to compute a HMAC + // Hacked by Lance Rushing + + $b = 64; // byte length for md5 + if (strlen($key) > $b) { + $key = pack('H*', md5($key)); } - return false; - } + $key = str_pad($key, $b, chr(0x00)); + $ipad = str_pad('', $b, chr(0x36)); + $opad = str_pad('', $b, chr(0x5c)); + $k_ipad = $key ^ $ipad ; + $k_opad = $key ^ $opad; - return true; + return md5($k_opad . pack('H*', md5($k_ipad . $data))); } /** @@ -272,11 +502,11 @@ class SMTP { */ public function Connected() { if(!empty($this->smtp_conn)) { - $sock_status = socket_get_status($this->smtp_conn); - if($sock_status["eof"]) { + $sock_status = stream_get_meta_data($this->smtp_conn); + if($sock_status['eof']) { // the socket is valid but we are not connected if($this->do_debug >= 1) { - echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; + $this->edebug('SMTP -> NOTICE: EOF caught while checking if connected'); } $this->Close(); return false; @@ -312,7 +542,7 @@ class SMTP { * finializing the mail transaction. $msg_data is the message * that is to be send with the headers. Each header needs to be * on a single line followed by a <CRLF> with the message headers - * and the message body being seperated by and additional <CRLF>. + * and the message body being separated by and additional <CRLF>. * * Implements rfc 821: DATA <CRLF> * @@ -320,10 +550,11 @@ class SMTP { * [data] * <CRLF>.<CRLF> * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 552,554,451,452 - * SMTP CODE FAILURE: 451,554 - * SMTP CODE ERROR : 500,501,503,421 + * SMTP CODE FAILURE: 552, 554, 451, 452 + * SMTP CODE FAILURE: 451, 554 + * SMTP CODE ERROR : 500, 501, 503, 421 * @access public + * @param string $msg_data * @return bool */ public function Data($msg_data) { @@ -331,26 +562,26 @@ class SMTP { if(!$this->connected()) { $this->error = array( - "error" => "Called Data() without being connected"); + 'error' => 'Called Data() without being connected'); return false; } - fputs($this->smtp_conn,"DATA" . $this->CRLF); + $this->client_send('DATA' . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 354) { $this->error = - array("error" => "DATA command not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'DATA command not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -367,9 +598,9 @@ class SMTP { */ // normalize the line breaks so we know the explode works - $msg_data = str_replace("\r\n","\n",$msg_data); - $msg_data = str_replace("\r","\n",$msg_data); - $lines = explode("\n",$msg_data); + $msg_data = str_replace("\r\n", "\n", $msg_data); + $msg_data = str_replace("\r", "\n", $msg_data); + $lines = explode("\n", $msg_data); /* we need to find a good way to determine is headers are * in the msg_data or if it is a straight msg body @@ -380,31 +611,31 @@ class SMTP { * headers. */ - $field = substr($lines[0],0,strpos($lines[0],":")); + $field = substr($lines[0], 0, strpos($lines[0], ':')); $in_headers = false; - if(!empty($field) && !strstr($field," ")) { + if(!empty($field) && !strstr($field, ' ')) { $in_headers = true; } $max_line_length = 998; // used below; set here for ease in change - while(list(,$line) = @each($lines)) { + while(list(, $line) = @each($lines)) { $lines_out = null; - if($line == "" && $in_headers) { + if($line == '' && $in_headers) { $in_headers = false; } // ok we need to break this line up into several smaller lines while(strlen($line) > $max_line_length) { - $pos = strrpos(substr($line,0,$max_line_length)," "); + $pos = strrpos(substr($line, 0, $max_line_length), ' '); // Patch to fix DOS attack if(!$pos) { $pos = $max_line_length - 1; - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos); + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos); } else { - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos + 1); + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos + 1); } /* if processing headers add a LWSP-char to the front of new line @@ -417,34 +648,34 @@ class SMTP { $lines_out[] = $line; // send the lines to the server - while(list(,$line_out) = @each($lines_out)) { + while(list(, $line_out) = @each($lines_out)) { if(strlen($line_out) > 0) { - if(substr($line_out, 0, 1) == ".") { - $line_out = "." . $line_out; + if(substr($line_out, 0, 1) == '.') { + $line_out = '.' . $line_out; } } - fputs($this->smtp_conn,$line_out . $this->CRLF); + $this->client_send($line_out . $this->CRLF); } } // message data has been sent - fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + $this->client_send($this->CRLF . '.' . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 250) { $this->error = - array("error" => "DATA not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'DATA not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -461,6 +692,7 @@ class SMTP { * SMTP CODE SUCCESS: 250 * SMTP CODE ERROR : 500, 501, 504, 421 * @access public + * @param string $host * @return bool */ public function Hello($host = '') { @@ -468,19 +700,19 @@ class SMTP { if(!$this->connected()) { $this->error = array( - "error" => "Called Hello() without being connected"); + 'error' => 'Called Hello() without being connected'); return false; } // if hostname for HELO was not specified send default if(empty($host)) { // determine appropriate default to send to server - $host = "localhost"; + $host = 'localhost'; } // Send extended hello first (RFC 2821) - if(!$this->SendHello("EHLO", $host)) { - if(!$this->SendHello("HELO", $host)) { + if(!$this->SendHello('EHLO', $host)) { + if(!$this->SendHello('HELO', $host)) { return false; } } @@ -490,26 +722,28 @@ class SMTP { /** * Sends a HELO/EHLO command. - * @access private + * @access protected + * @param string $hello + * @param string $host * @return bool */ - private function SendHello($hello, $host) { - fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + protected function SendHello($hello, $host) { + $this->client_send($hello . ' ' . $host . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER: ' . $rply); } if($code != 250) { $this->error = - array("error" => $hello . " not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => $hello . ' not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -528,9 +762,10 @@ class SMTP { * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> * * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,421 + * SMTP CODE SUCCESS: 552, 451, 452 + * SMTP CODE SUCCESS: 500, 501, 421 * @access public + * @param string $from * @return bool */ public function Mail($from) { @@ -538,27 +773,27 @@ class SMTP { if(!$this->connected()) { $this->error = array( - "error" => "Called Mail() without being connected"); + 'error' => 'Called Mail() without being connected'); return false; } - $useVerp = ($this->do_verp ? "XVERP" : ""); - fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); + $useVerp = ($this->do_verp ? ' XVERP' : ''); + $this->client_send('MAIL FROM:<' . $from . '>' . $useVerp . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 250) { $this->error = - array("error" => "MAIL not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'MAIL not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -574,6 +809,7 @@ class SMTP { * SMTP CODE SUCCESS: 221 * SMTP CODE ERROR : 500 * @access public + * @param bool $close_on_error * @return bool */ public function Quit($close_on_error = true) { @@ -581,32 +817,32 @@ class SMTP { if(!$this->connected()) { $this->error = array( - "error" => "Called Quit() without being connected"); + 'error' => 'Called Quit() without being connected'); return false; } // send the quit command to the server - fputs($this->smtp_conn,"quit" . $this->CRLF); + $this->client_send('quit' . $this->CRLF); // get any good-bye messages $byemsg = $this->get_lines(); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $byemsg); } $rval = true; $e = null; - $code = substr($byemsg,0,3); + $code = substr($byemsg, 0, 3); if($code != 221) { // use e as a tmp var cause Close will overwrite $this->error - $e = array("error" => "SMTP server rejected quit command", - "smtp_code" => $code, - "smtp_rply" => substr($byemsg,4)); + $e = array('error' => 'SMTP server rejected quit command', + 'smtp_code' => $code, + 'smtp_rply' => substr($byemsg, 4)); $rval = false; if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $e['error'] . ': ' . $byemsg); } } @@ -623,10 +859,11 @@ class SMTP { * * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,552,553,450,451,452 - * SMTP CODE ERROR : 500,501,503,421 + * SMTP CODE SUCCESS: 250, 251 + * SMTP CODE FAILURE: 550, 551, 552, 553, 450, 451, 452 + * SMTP CODE ERROR : 500, 501, 503, 421 * @access public + * @param string $to * @return bool */ public function Recipient($to) { @@ -634,26 +871,26 @@ class SMTP { if(!$this->connected()) { $this->error = array( - "error" => "Called Recipient() without being connected"); + 'error' => 'Called Recipient() without being connected'); return false; } - fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + $this->client_send('RCPT TO:<' . $to . '>' . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 250 && $code != 251) { $this->error = - array("error" => "RCPT not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'RCPT not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -668,7 +905,7 @@ class SMTP { * Implements rfc 821: RSET <CRLF> * * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500,501,504,421 + * SMTP CODE ERROR : 500, 501, 504, 421 * @access public * @return bool */ @@ -676,27 +913,26 @@ class SMTP { $this->error = null; // so no confusion is caused if(!$this->connected()) { - $this->error = array( - "error" => "Called Reset() without being connected"); + $this->error = array('error' => 'Called Reset() without being connected'); return false; } - fputs($this->smtp_conn,"RSET" . $this->CRLF); + $this->client_send('RSET' . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 250) { $this->error = - array("error" => "RSET failed", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'RSET failed', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -715,9 +951,10 @@ class SMTP { * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> * * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 + * SMTP CODE SUCCESS: 552, 451, 452 + * SMTP CODE SUCCESS: 500, 501, 502, 421 * @access public + * @param string $from * @return bool */ public function SendAndMail($from) { @@ -725,26 +962,26 @@ class SMTP { if(!$this->connected()) { $this->error = array( - "error" => "Called SendAndMail() without being connected"); + 'error' => 'Called SendAndMail() without being connected'); return false; } - fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + $this->client_send('SAML FROM:' . $from . $this->CRLF); $rply = $this->get_lines(); - $code = substr($rply,0,3); + $code = substr($rply, 0, 3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> FROM SERVER:' . $rply); } if($code != 250) { $this->error = - array("error" => "SAML not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); + array('error' => 'SAML not accepted from server', + 'smtp_code' => $code, + 'smtp_msg' => substr($rply, 4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'; + $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply); } return false; } @@ -765,15 +1002,28 @@ class SMTP { * @return bool */ public function Turn() { - $this->error = array("error" => "This method, TURN, of the SMTP ". - "is not implemented"); + $this->error = array('error' => 'This method, TURN, of the SMTP '. + 'is not implemented'); if($this->do_debug >= 1) { - echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '<br />'; + $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); } return false; } /** + * Sends data to the server + * @param string $data + * @access public + * @return Integer number of bytes sent to the server or FALSE on error + */ + public function client_send($data) { + if ($this->do_debug >= 1) { + $this->edebug("CLIENT -> SMTP: $data"); + } + return fwrite($this->smtp_conn, $data); + } + + /** * Get the current error * @access public * @return array @@ -792,26 +1042,51 @@ class SMTP { * With SMTP we can tell if we have more lines to read if the * 4th character is '-' symbol. If it is a space then we don't * need to read anything else. - * @access private + * @access protected * @return string */ - private function get_lines() { - $data = ""; - while($str = @fgets($this->smtp_conn,515)) { + protected function get_lines() { + $data = ''; + $endtime = 0; + /* If for some reason the fp is bad, don't inf loop */ + if (!is_resource($this->smtp_conn)) { + return $data; + } + stream_set_timeout($this->smtp_conn, $this->Timeout); + if ($this->Timelimit > 0) { + $endtime = time() + $this->Timelimit; + } + while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { + $str = @fgets($this->smtp_conn, 515); if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '<br />'; - echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '<br />'; + $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); } $data .= $str; if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '<br />'; + $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); } // if 4th character is a space, we are done reading, break the loop - if(substr($str,3,1) == " ") { break; } + if(substr($str, 3, 1) == ' ') { break; } + // Timed-out? Log and break + $info = stream_get_meta_data($this->smtp_conn); + if ($info['timed_out']) { + if($this->do_debug >= 4) { + $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' seconds)'); + } + break; + } + // Now check if reads took too long + if ($endtime) { + if (time() > $endtime) { + if($this->do_debug >= 4) { + $this->edebug('SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' seconds)'); + } + break; + } + } } return $data; } } - -?> diff --git a/library/phpmailer/language/phpmailer.lang-ar.php b/library/phpmailer/language/phpmailer.lang-ar.php index b7c5057d0c..67364e967a 100644 --- a/library/phpmailer/language/phpmailer.lang-ar.php +++ b/library/phpmailer/language/phpmailer.lang-ar.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = 'لم نستطع الوصول للمل $PHPMAILER_LANG['file_open'] = 'File Error: لم نستطع فتح الملف: '; $PHPMAILER_LANG['from_failed'] = 'البريد التالي لم نستطع ارسال البريد له : '; $PHPMAILER_LANG['instantiate'] = 'لم نستطع توفير خدمة البريد.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer غير مدعوم.'; //$PHPMAILER_LANG['provide_address'] = 'You must provide at least one recipient email address.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: الأخطاء التالية ' . @@ -24,4 +24,3 @@ $PHPMAILER_LANG['signing'] = 'خطأ في التوقيع: '; //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-br.php b/library/phpmailer/language/phpmailer.lang-br.php index 6afe60b18c..87ecbdfa5e 100644 --- a/library/phpmailer/language/phpmailer.lang-br.php +++ b/library/phpmailer/language/phpmailer.lang-br.php @@ -3,24 +3,24 @@ * PHPMailer language file: refer to English translation for definitive list * Portuguese Version * By Paulo Henrique Garcia - paulo@controllerweb.com.br +* Edited by Lucas Guimarães - lucas@lucasguimaraes.com */ $PHPMAILER_LANG['authenticate'] = 'Erro de SMTP: Não foi possível autenticar.'; $PHPMAILER_LANG['connect_host'] = 'Erro de SMTP: Não foi possível conectar com o servidor SMTP.'; -$PHPMAILER_LANG['data_not_accepted'] = 'Erro de SMTP: Dados não aceitos.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['data_not_accepted'] = 'Erro de SMTP: Dados rejeitados.'; +$PHPMAILER_LANG['empty_message'] = 'Corpo da mensagem vazio'; $PHPMAILER_LANG['encoding'] = 'Codificação desconhecida: '; $PHPMAILER_LANG['execute'] = 'Não foi possível executar: '; $PHPMAILER_LANG['file_access'] = 'Não foi possível acessar o arquivo: '; $PHPMAILER_LANG['file_open'] = 'Erro de Arquivo: Não foi possível abrir o arquivo: '; -$PHPMAILER_LANG['from_failed'] = 'Os endereços de rementente a seguir falharam: '; -$PHPMAILER_LANG['instantiate'] = 'Não foi possível instanciar a função mail.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; -$PHPMAILER_LANG['mailer_not_supported'] = ' mailer não suportado.'; -$PHPMAILER_LANG['provide_address'] = 'Você deve fornecer pelo menos um endereço de destinatário de email.'; +$PHPMAILER_LANG['from_failed'] = 'Os endereços dos remententes a seguir falharam: '; +$PHPMAILER_LANG['instantiate'] = 'Não foi possível iniciar uma instância da função mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Não enviando, endereço de e-mail inválido: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer não é suportado.'; +$PHPMAILER_LANG['provide_address'] = 'Você deve fornecer pelo menos um endereço de destinatário de e-mail.'; $PHPMAILER_LANG['recipients_failed'] = 'Erro de SMTP: Os endereços de destinatário a seguir falharam: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['signing'] = 'Erro ao assinar: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() falhou.'; +$PHPMAILER_LANG['smtp_error'] = 'Erro de servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Não foi possível definir ou resetar a variável: '; diff --git a/library/phpmailer/language/phpmailer.lang-ca.php b/library/phpmailer/language/phpmailer.lang-ca.php index 4a160a21eb..dc563e77f6 100644 --- a/library/phpmailer/language/phpmailer.lang-ca.php +++ b/library/phpmailer/language/phpmailer.lang-ca.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = 'No es pot accedir a l\'arxiu: '; $PHPMAILER_LANG['file_open'] = 'Error d\'Arxiu: No es pot obrir l\'arxiu: '; $PHPMAILER_LANG['from_failed'] = 'La(s) següent(s) adreces de remitent han fallat: '; $PHPMAILER_LANG['instantiate'] = 'No s\'ha pogut crear una instància de la funció Mail.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer no està suportat'; $PHPMAILER_LANG['provide_address'] = 'S\'ha de proveir almenys una adreça d\'email com a destinatari.'; $PHPMAILER_LANG['recipients_failed'] = 'Error SMTP: Els següents destinataris han fallat: '; @@ -23,4 +23,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'Error SMTP: Els següents destinatari //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-ch.php b/library/phpmailer/language/phpmailer.lang-ch.php index 31ebd861cb..d28eba611b 100644 --- a/library/phpmailer/language/phpmailer.lang-ch.php +++ b/library/phpmailer/language/phpmailer.lang-ch.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = '不能访问文件:'; $PHPMAILER_LANG['file_open'] = '文件错误:不能打开文件:'; $PHPMAILER_LANG['from_failed'] = '下面的发送地址邮件发送失败了: '; $PHPMAILER_LANG['instantiate'] = '不能实现mail方法。'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' 您所选择的发送邮件的方法并不支持。'; $PHPMAILER_LANG['provide_address'] = '您必须提供至少一个 收信人的email地址。'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP 错误: 下面的 收件人失败了: '; @@ -23,4 +23,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP 错误: 下面的 收件人失败 //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-cz.php b/library/phpmailer/language/phpmailer.lang-cz.php index 1c8b206392..b1f0a51c28 100644 --- a/library/phpmailer/language/phpmailer.lang-cz.php +++ b/library/phpmailer/language/phpmailer.lang-cz.php @@ -14,7 +14,7 @@ $PHPMAILER_LANG['file_access'] = 'Soubor nenalezen: '; $PHPMAILER_LANG['file_open'] = 'File Error: Nelze otevøít soubor pro ètení: '; $PHPMAILER_LANG['from_failed'] = 'Následující adresa From je nesprávná: '; $PHPMAILER_LANG['instantiate'] = 'Nelze vytvoøit instanci emailové funkce.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' mailový klient není podporován.'; $PHPMAILER_LANG['provide_address'] = 'Musíte zadat alespoò jednu emailovou adresu pøíjemce.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: Adresy pøíjemcù nejsou správné '; @@ -22,4 +22,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: Adresy pøíjemcù nejsou //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-de.php b/library/phpmailer/language/phpmailer.lang-de.php index b2a76ce1b5..ba17a92a95 100644 --- a/library/phpmailer/language/phpmailer.lang-de.php +++ b/library/phpmailer/language/phpmailer.lang-de.php @@ -14,7 +14,7 @@ $PHPMAILER_LANG['file_access'] = 'Zugriff auf folgende Datei fehlgeschl $PHPMAILER_LANG['file_open'] = 'Datei Fehler: konnte folgende Datei nicht öffnen: '; $PHPMAILER_LANG['from_failed'] = 'Die folgende Absenderadresse ist nicht korrekt: '; $PHPMAILER_LANG['instantiate'] = 'Mail Funktion konnte nicht initialisiert werden.'; -$PHPMAILER_LANG['invalid_email'] = 'E-Mail wird nicht gesendet, die Adresse ist ungültig.'; +$PHPMAILER_LANG['invalid_address'] = 'E-Mail wird nicht gesendet, die Adresse ist ungültig.'; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer wird nicht unterstützt.'; $PHPMAILER_LANG['provide_address'] = 'Bitte geben Sie mindestens eine Empfänger E-Mailadresse an.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP Fehler: Die folgenden Empfänger sind nicht korrekt: '; @@ -22,4 +22,3 @@ $PHPMAILER_LANG['signing'] = 'Fehler beim Signieren: '; $PHPMAILER_LANG['smtp_connect_failed'] = 'Verbindung zu SMTP Server fehlgeschlagen.'; $PHPMAILER_LANG['smtp_error'] = 'Fehler vom SMTP Server: '; $PHPMAILER_LANG['variable_set'] = 'Kann Variable nicht setzen oder zurücksetzen: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-dk.php b/library/phpmailer/language/phpmailer.lang-dk.php index b26257316b..fdb0b17b85 100644 --- a/library/phpmailer/language/phpmailer.lang-dk.php +++ b/library/phpmailer/language/phpmailer.lang-dk.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = 'Ingen adgang til fil: '; $PHPMAILER_LANG['file_open'] = 'Fil fejl: Kunne ikke åbne filen: '; $PHPMAILER_LANG['from_failed'] = 'Følgende afsenderadresse er forkert: '; $PHPMAILER_LANG['instantiate'] = 'Kunne ikke initialisere email funktionen.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer understøttes ikke.'; $PHPMAILER_LANG['provide_address'] = 'Du skal indtaste mindst en modtagers emailadresse.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP fejl: Følgende modtagere er forkerte: '; @@ -23,4 +23,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP fejl: Følgende modtagere er for //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-eo.php b/library/phpmailer/language/phpmailer.lang-eo.php new file mode 100644 index 0000000000..799b331963 --- /dev/null +++ b/library/phpmailer/language/phpmailer.lang-eo.php @@ -0,0 +1,24 @@ +<?php +/** +* PHPMailer language file: refer to English translation for definitive list +* Esperanto version +*/ + +$PHPMAILER_LANG['authenticate'] = 'Eraro de servilo SMTP : aŭtentigo malsukcesis.'; +$PHPMAILER_LANG['connect_host'] = 'Eraro de servilo SMTP : konektado al servilo malsukcesis.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Eraro de servilo SMTP : neĝustaj datumoj.'; +$PHPMAILER_LANG['empty_message'] = 'Teksto de mesaĝo mankas.'; +$PHPMAILER_LANG['encoding'] = 'Nekonata kodoprezento: '; +$PHPMAILER_LANG['execute'] = 'Lanĉi rulumadon ne eblis: '; +$PHPMAILER_LANG['file_access'] = 'Aliro al dosiero ne sukcesis: '; +$PHPMAILER_LANG['file_open'] = 'Eraro de dosiero: malfermo neeblas: '; +$PHPMAILER_LANG['from_failed'] = 'Jena adreso de sendinto malsukcesis: '; +$PHPMAILER_LANG['instantiate'] = 'Genero de retmesaĝa funkcio neeblis.'; +$PHPMAILER_LANG['invalid_address'] = 'Retadreso ne validas: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mesaĝilo ne subtenata.'; +$PHPMAILER_LANG['provide_address'] = 'Vi devas tajpi almenaŭ unu recevontan retadreson.'; +$PHPMAILER_LANG['recipients_failed'] = 'Eraro de servilo SMTP : la jenaj poŝtrecivuloj kaŭzis eraron: '; +$PHPMAILER_LANG['signing'] = 'Eraro de subskribo: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP konektado malsukcesis.'; +$PHPMAILER_LANG['smtp_error'] = 'Eraro de servilo SMTP : '; +$PHPMAILER_LANG['variable_set'] = 'Variablo ne pravalorizeblas aŭ ne repravalorizeblas: '; diff --git a/library/phpmailer/language/phpmailer.lang-es.php b/library/phpmailer/language/phpmailer.lang-es.php index 69b6817482..b81520f285 100644 --- a/library/phpmailer/language/phpmailer.lang-es.php +++ b/library/phpmailer/language/phpmailer.lang-es.php @@ -3,24 +3,24 @@ * PHPMailer language file: refer to English translation for definitive list * Spanish version * Versión en español +* Edited by Matt Sturdy - matt.sturdy@gmail.com */ $PHPMAILER_LANG['authenticate'] = 'Error SMTP: No se pudo autentificar.'; -$PHPMAILER_LANG['connect_host'] = 'Error SMTP: No puedo conectar al servidor SMTP.'; +$PHPMAILER_LANG['connect_host'] = 'Error SMTP: No se pudo conectar al servidor SMTP.'; $PHPMAILER_LANG['data_not_accepted'] = 'Error SMTP: Datos no aceptados.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['empty_message'] = 'Cuerpo del mensaje vacío'; $PHPMAILER_LANG['encoding'] = 'Codificación desconocida: '; -$PHPMAILER_LANG['execute'] = 'No puedo ejecutar: '; -$PHPMAILER_LANG['file_access'] = 'No puedo acceder al archivo: '; -$PHPMAILER_LANG['file_open'] = 'Error de Archivo: No puede abrir el archivo: '; +$PHPMAILER_LANG['execute'] = 'No se pudo ejecutar: '; +$PHPMAILER_LANG['file_access'] = 'No se pudo acceder al archivo: '; +$PHPMAILER_LANG['file_open'] = 'Error de Archivo: No se pudo abrir el archivo: '; $PHPMAILER_LANG['from_failed'] = 'La(s) siguiente(s) direcciones de remitente fallaron: '; -$PHPMAILER_LANG['instantiate'] = 'No pude crear una instancia de la función Mail.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +$PHPMAILER_LANG['instantiate'] = 'No se pudo crear una instancia de la función Mail.'; +$PHPMAILER_LANG['invalid_address'] = 'No se pudo enviar: dirección de email inválido: '; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer no está soportado.'; -$PHPMAILER_LANG['provide_address'] = 'Debe proveer al menos una dirección de email como destinatario.'; -$PHPMAILER_LANG['recipients_failed'] = 'Error SMTP: Los siguientes destinatarios fallaron: '; +$PHPMAILER_LANG['provide_address'] = 'Debe proveer al menos una dirección de email como destino.'; +$PHPMAILER_LANG['recipients_failed'] = 'Error SMTP: Los siguientes destinos fallaron: '; $PHPMAILER_LANG['signing'] = 'Error al firmar: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() se falló.'; +$PHPMAILER_LANG['smtp_error'] = 'Error del servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'No se pudo ajustar o reajustar la variable: '; diff --git a/library/phpmailer/language/phpmailer.lang-et.php b/library/phpmailer/language/phpmailer.lang-et.php index cf61779b08..eee4e58e88 100644 --- a/library/phpmailer/language/phpmailer.lang-et.php +++ b/library/phpmailer/language/phpmailer.lang-et.php @@ -3,24 +3,24 @@ * PHPMailer language file: refer to English translation for definitive list * Estonian Version * By Indrek Päri +* Revised By Elan Ruusamäe <glen@delfi.ee> */ $PHPMAILER_LANG['authenticate'] = 'SMTP Viga: Autoriseerimise viga.'; $PHPMAILER_LANG['connect_host'] = 'SMTP Viga: Ei õnnestunud luua ühendust SMTP serveriga.'; $PHPMAILER_LANG['data_not_accepted'] = 'SMTP Viga: Vigased andmed.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; -$PHPMAILER_LANG['encoding'] = 'Tundmatu Unknown kodeering: '; +$PHPMAILER_LANG['empty_message'] = 'Tühi kirja sisu'; +$PHPMAILER_LANG["encoding"] = 'Tundmatu kodeering: '; $PHPMAILER_LANG['execute'] = 'Tegevus ebaõnnestus: '; $PHPMAILER_LANG['file_access'] = 'Pole piisavalt õiguseid järgneva faili avamiseks: '; $PHPMAILER_LANG['file_open'] = 'Faili Viga: Faili avamine ebaõnnestus: '; $PHPMAILER_LANG['from_failed'] = 'Järgnev saatja e-posti aadress on vigane: '; $PHPMAILER_LANG['instantiate'] = 'mail funktiooni käivitamine ebaõnnestus.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +$PHPMAILER_LANG['invalid_address'] = 'Saatmine peatatud, e-posti address vigane: '; $PHPMAILER_LANG['provide_address'] = 'Te peate määrama vähemalt ühe saaja e-posti aadressi.'; $PHPMAILER_LANG['mailer_not_supported'] = ' maileri tugi puudub.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP Viga: Järgnevate saajate e-posti aadressid on vigased: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG["signing"] = 'Viga allkirjastamisel: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() ebaõnnestus.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP serveri viga: '; +$PHPMAILER_LANG['variable_set'] = 'Ei õnnestunud määrata või lähtestada muutujat: '; diff --git a/library/phpmailer/language/phpmailer.lang-fi.php b/library/phpmailer/language/phpmailer.lang-fi.php index 12a845aad6..16a02e1380 100644 --- a/library/phpmailer/language/phpmailer.lang-fi.php +++ b/library/phpmailer/language/phpmailer.lang-fi.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = 'Seuraavaan tiedostoon ei ole oikeuksi $PHPMAILER_LANG['file_open'] = 'Tiedostovirhe: Ei voida avata tiedostoa: '; $PHPMAILER_LANG['from_failed'] = 'Seuraava lähettäjän osoite on virheellinen: '; $PHPMAILER_LANG['instantiate'] = 'mail-funktion luonti epäonnistui.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = 'postivälitintyyppiä ei tueta.'; $PHPMAILER_LANG['provide_address'] = 'Aseta vähintään yksi vastaanottajan sähköpostiosoite.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP-virhe: seuraava vastaanottaja osoite on virheellinen.'; @@ -24,4 +24,3 @@ $PHPMAILER_LANG['encoding'] = 'Tuntematon koodaustyyppi: '; //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-fo.php b/library/phpmailer/language/phpmailer.lang-fo.php index 6bd9b0a213..fc5d6d5654 100644 --- a/library/phpmailer/language/phpmailer.lang-fo.php +++ b/library/phpmailer/language/phpmailer.lang-fo.php @@ -16,7 +16,7 @@ $PHPMAILER_LANG['file_access'] = 'Kundi ikki tilganga fílu: '; $PHPMAILER_LANG['file_open'] = 'Fílu feilur: Kundi ikki opna fílu: '; $PHPMAILER_LANG['from_failed'] = 'fylgjandi Frá/From adressa miseydnaðist: '; $PHPMAILER_LANG['instantiate'] = 'Kuni ikki instantiera mail funktión.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' er ikki supporterað.'; $PHPMAILER_LANG['provide_address'] = 'Tú skal uppgeva minst móttakara-emailadressu(r).'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP Feilur: Fylgjandi móttakarar miseydnaðust: '; @@ -24,4 +24,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP Feilur: Fylgjandi móttakarar mi //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-fr.php b/library/phpmailer/language/phpmailer.lang-fr.php index c99ac3caf9..b8dfc7743d 100644 --- a/library/phpmailer/language/phpmailer.lang-fr.php +++ b/library/phpmailer/language/phpmailer.lang-fr.php @@ -4,22 +4,21 @@ * French Version */ -$PHPMAILER_LANG['authenticate'] = 'Erreur SMTP : Echec de l\'authentification.'; -$PHPMAILER_LANG['connect_host'] = 'Erreur SMTP : Impossible de se connecter au serveur SMTP.'; -$PHPMAILER_LANG['data_not_accepted'] = 'Erreur SMTP : Données incorrects.'; -$PHPMAILER_LANG['empty_message'] = 'Corps de message vide'; -$PHPMAILER_LANG['encoding'] = 'Encodage inconnu : '; -$PHPMAILER_LANG['execute'] = 'Impossible de lancer l\'exécution : '; -$PHPMAILER_LANG['file_access'] = 'Impossible d\'accéder au fichier : '; -$PHPMAILER_LANG['file_open'] = 'Erreur Fichier : ouverture impossible : '; -$PHPMAILER_LANG['from_failed'] = 'L\'adresse d\'expéditeur suivante a échouée : '; +$PHPMAILER_LANG['authenticate'] = 'Erreur SMTP : échec de l\'authentification.'; +$PHPMAILER_LANG['connect_host'] = 'Erreur SMTP : impossible de se connecter au serveur SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Erreur SMTP : données incorrectes.'; +$PHPMAILER_LANG['empty_message'] = 'Corps de message vide.'; +$PHPMAILER_LANG['encoding'] = 'Encodage inconnu: '; +$PHPMAILER_LANG['execute'] = 'Impossible de lancer l\'exécution: '; +$PHPMAILER_LANG['file_access'] = 'Impossible d\'accéder au fichier: '; +$PHPMAILER_LANG['file_open'] = 'Erreur de fichier : ouverture impossible: '; +$PHPMAILER_LANG['from_failed'] = 'L\'adresse d\'expéditeur suivante a échouée: '; $PHPMAILER_LANG['instantiate'] = 'Impossible d\'instancier la fonction mail.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +$PHPMAILER_LANG['invalid_address'] = 'L\'adresse courriel n\'est pas valide: '; $PHPMAILER_LANG['mailer_not_supported'] = ' client de messagerie non supporté.'; $PHPMAILER_LANG['provide_address'] = 'Vous devez fournir au moins une adresse de destinataire.'; -$PHPMAILER_LANG['recipients_failed'] = 'Erreur SMTP : Les destinataires suivants sont en erreur : '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['recipients_failed'] = 'Erreur SMTP : les destinataires suivants sont en erreur: '; +$PHPMAILER_LANG['signing'] = 'Erreur de signature: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Échec de la connexion SMTP.'; +$PHPMAILER_LANG['smtp_error'] = 'Erreur du serveur SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Ne peut initialiser ou réinitialiser une variable: '; diff --git a/library/phpmailer/language/phpmailer.lang-he.php b/library/phpmailer/language/phpmailer.lang-he.php new file mode 100644 index 0000000000..e30b596d60 --- /dev/null +++ b/library/phpmailer/language/phpmailer.lang-he.php @@ -0,0 +1,25 @@ +<?php +/** +* PHPMailer language file: refer to English translation for definitive list +* Hebrew Version, UTF-8 +* by : Ronny Sherer <ronny@hoojima.com> +*/ + +$PHPMAILER_LANG['authenticate'] = 'שגיאת SMTP: פעולת האימות נכשלה.'; +$PHPMAILER_LANG['connect_host'] = 'שגיאת SMTP: לא הצלחתי להתחבר לשרת SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'שגיאת SMTP: מידע לא התקבל.'; +$PHPMAILER_LANG['empty_message'] = 'גוף ההודעה ריק'; +$PHPMAILER_LANG['invalid_address'] = 'כתובת שגויה'; +$PHPMAILER_LANG['encoding'] = 'קידוד לא מוכר: '; +$PHPMAILER_LANG['execute'] = 'לא הצלחתי להפעיל את: '; +$PHPMAILER_LANG['file_access'] = 'לא ניתן לגשת לקובץ: '; +$PHPMAILER_LANG['file_open'] = 'שגיאת קובץ: לא ניתן לגשת לקובץ: '; +$PHPMAILER_LANG['from_failed'] = 'כתובות הנמענים הבאות נכשלו: '; +$PHPMAILER_LANG['instantiate'] = 'לא הצלחתי להפעיל את פונקציית המייל.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' אינה נתמכת.'; +$PHPMAILER_LANG['provide_address'] = 'חובה לספק לפחות כתובת אחת של מקבל המייל.'; +$PHPMAILER_LANG['recipients_failed'] = 'שגיאת SMTP: הנמענים הבאים נכשלו: '; +$PHPMAILER_LANG['signing'] = 'שגיאת חתימה: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; +$PHPMAILER_LANG['smtp_error'] = 'שגיאת שרת SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'לא ניתן לקבוע או לשנות את המשתנה: '; diff --git a/library/phpmailer/language/phpmailer.lang-hu.php b/library/phpmailer/language/phpmailer.lang-hu.php index caca0b50f1..d8d1926796 100644 --- a/library/phpmailer/language/phpmailer.lang-hu.php +++ b/library/phpmailer/language/phpmailer.lang-hu.php @@ -1,25 +1,24 @@ <?php /** * PHPMailer language file: refer to English translation for definitive list -* Hungarian Version +* Hungarian Version by @dominicus-75 */ -$PHPMAILER_LANG['authenticate'] = 'SMTP Hiba: Sikertelen autentikáció.'; -$PHPMAILER_LANG['connect_host'] = 'SMTP Hiba: Nem tudtam csatlakozni az SMTP host-hoz.'; -$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Hiba: Nem elfogadható adat.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['authenticate'] = 'SMTP hiba: az azonosítás sikertelen.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP hiba: nem lehet kapcsolódni az SMTP-szerverhez.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP hiba: adatok visszautasítva.'; +$PHPMAILER_LANG['empty_message'] = 'Üres az üzenettörzs.'; $PHPMAILER_LANG['encoding'] = 'Ismeretlen kódolás: '; -$PHPMAILER_LANG['execute'] = 'Nem tudtam végrehajtani: '; -$PHPMAILER_LANG['file_access'] = 'Nem sikerült elérni a következõ fájlt: '; -$PHPMAILER_LANG['file_open'] = 'Fájl Hiba: Nem sikerült megnyitni a következõ fájlt: '; -$PHPMAILER_LANG['from_failed'] = 'Az alábbi Feladó cím hibás: '; -$PHPMAILER_LANG['instantiate'] = 'Nem sikerült példányosítani a mail funkciót.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; -$PHPMAILER_LANG['provide_address'] = 'Meg kell adnod legalább egy címzett email címet.'; -$PHPMAILER_LANG['mailer_not_supported'] = ' levelezõ nem támogatott.'; -$PHPMAILER_LANG['recipients_failed'] = 'SMTP Hiba: Az alábbi címzettek hibásak: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['execute'] = 'Nem lehet végrehajtani: '; +$PHPMAILER_LANG['file_access'] = 'A következő fájl nem elérhető: '; +$PHPMAILER_LANG['file_open'] = 'Fájl hiba: a következő fájlt nem lehet megnyitni: '; +$PHPMAILER_LANG['from_failed'] = 'A feladóként megadott következő cím hibás: '; +$PHPMAILER_LANG['instantiate'] = 'A PHP mail() függvényt nem sikerült végrehajtani.'; +$PHPMAILER_LANG['invalid_address'] = 'Érvénytelen cím: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' a mailer-osztály nem támogatott.'; +$PHPMAILER_LANG['provide_address'] = 'Legalább egy címzettet fel kell tüntetni.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP hiba: a címzettként megadott következő címek hibásak: '; +$PHPMAILER_LANG['signing'] = 'Hibás aláírás: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Hiba az SMTP-kapcsolatban.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP-szerver hiba: '; +$PHPMAILER_LANG['variable_set'] = 'A következő változók beállítása nem sikerült: '; diff --git a/library/phpmailer/language/phpmailer.lang-it.php b/library/phpmailer/language/phpmailer.lang-it.php index fc1fcb8d2e..27ab0ead00 100644 --- a/library/phpmailer/language/phpmailer.lang-it.php +++ b/library/phpmailer/language/phpmailer.lang-it.php @@ -3,25 +3,24 @@ * PHPMailer language file: refer to English translation for definitive list * Italian version * @package PHPMailer -* @author Ilias Bartolini <brain79@inwind.it> +* @author Ilias Bartolini <brain79@inwind.it>, Stefano Sabatini <sabas88@gmail.com> */ $PHPMAILER_LANG['authenticate'] = 'SMTP Error: Impossibile autenticarsi.'; $PHPMAILER_LANG['connect_host'] = 'SMTP Error: Impossibile connettersi all\'host SMTP.'; -$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Error: Data non accettati dal server.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; -$PHPMAILER_LANG['encoding'] = 'Encoding set dei caratteri sconosciuto: '; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Error: Dati non accettati dal server.'; +$PHPMAILER_LANG['empty_message'] = 'Il corpo del messaggio è vuoto'; +$PHPMAILER_LANG['encoding'] = 'Codifica dei caratteri sconosciuta: '; $PHPMAILER_LANG['execute'] = 'Impossibile eseguire l\'operazione: '; $PHPMAILER_LANG['file_access'] = 'Impossibile accedere al file: '; $PHPMAILER_LANG['file_open'] = 'File Error: Impossibile aprire il file: '; $PHPMAILER_LANG['from_failed'] = 'I seguenti indirizzi mittenti hanno generato errore: '; $PHPMAILER_LANG['instantiate'] = 'Impossibile istanziare la funzione mail'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +$PHPMAILER_LANG['invalid_address'] = 'Impossibile inviare, l\'indirizzo email non è valido: '; $PHPMAILER_LANG['provide_address'] = 'Deve essere fornito almeno un indirizzo ricevente'; $PHPMAILER_LANG['mailer_not_supported'] = 'Mailer non supportato'; -$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: I seguenti indirizzi destinatari hanno generato errore: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: I seguenti indirizzi destinatari hanno generato un errore: '; +$PHPMAILER_LANG['signing'] = 'Errore nella firma: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() fallita.'; +$PHPMAILER_LANG['smtp_error'] = 'Errore del server SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Impossibile impostare o resettare la variabile: '; diff --git a/library/phpmailer/language/phpmailer.lang-ja.php b/library/phpmailer/language/phpmailer.lang-ja.php index 63cfb23b6a..ca6de909da 100644 --- a/library/phpmailer/language/phpmailer.lang-ja.php +++ b/library/phpmailer/language/phpmailer.lang-ja.php @@ -3,6 +3,7 @@ * PHPMailer language file: refer to English translation for definitive list * Japanese Version * By Mitsuhiro Yoshida - http://mitstek.com/ +* Modified by Yoshi Sakai - http://bluemooninc.jp/ */ $PHPMAILER_LANG['authenticate'] = 'SMTPエラー: 認証できませんでした。'; @@ -13,9 +14,9 @@ $PHPMAILER_LANG['encoding'] = '不明なエンコーディング: '; $PHPMAILER_LANG['execute'] = '実行できませんでした: '; $PHPMAILER_LANG['file_access'] = 'ファイルにアクセスできません: '; $PHPMAILER_LANG['file_open'] = 'ファイルエラー: ファイルを開けません: '; -$PHPMAILER_LANG['from_failed'] = '次のFromアドレスに間違いがあります: '; +$PHPMAILER_LANG['from_failed'] = 'Fromアドレスを登録する際にエラーが発生しました: '; $PHPMAILER_LANG['instantiate'] = 'メール関数が正常に動作しませんでした。'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['provide_address'] = '少なくとも1つメールアドレスを 指定する必要があります。'; $PHPMAILER_LANG['mailer_not_supported'] = ' メーラーがサポートされていません。'; $PHPMAILER_LANG['recipients_failed'] = 'SMTPエラー: 次の受信者アドレスに 間違いがあります: '; @@ -23,4 +24,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTPエラー: 次の受信者アドレ //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-lt.php b/library/phpmailer/language/phpmailer.lang-lt.php new file mode 100644 index 0000000000..5dba58a932 --- /dev/null +++ b/library/phpmailer/language/phpmailer.lang-lt.php @@ -0,0 +1,24 @@ +<?php +/** +* PHPMailer language file: refer to English translation for definitive list +* Lithuanian version by Dainius Kaupaitis <dk@sum.lt> +*/ + +$PHPMAILER_LANG['authenticate'] = 'SMTP klaida: autentifikacija nepavyko.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP klaida: nepavyksta prisijungti prie SMTP stoties.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP klaida: duomenys nepriimti.'; +$PHPMAILER_LANG['empty_message'] = 'Laiško turinys tuščias'; +$PHPMAILER_LANG['encoding'] = 'Neatpažinta koduotė: '; +$PHPMAILER_LANG['execute'] = 'Nepavyko įvykdyti komandos: '; +$PHPMAILER_LANG['file_access'] = 'Byla nepasiekiama: '; +$PHPMAILER_LANG['file_open'] = 'Bylos klaida: Nepavyksta atidaryti: '; +$PHPMAILER_LANG['from_failed'] = 'Neteisingas siuntėjo adresas: '; +$PHPMAILER_LANG['instantiate'] = 'Nepavyko paleisti mail funkcijos.'; +$PHPMAILER_LANG['invalid_address'] = 'Neteisingas adresas'; +$PHPMAILER_LANG['mailer_not_supported'] = ' pašto stotis nepalaikoma.'; +$PHPMAILER_LANG['provide_address'] = 'Nurodykite bent vieną gavėjo adresą.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP klaida: nepavyko išsiųsti šiems gavėjams: '; +$PHPMAILER_LANG['signing'] = 'Prisijungimo klaida: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP susijungimo klaida'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP stoties klaida: '; +$PHPMAILER_LANG['variable_set'] = 'Nepavyko priskirti reikšmės kintamajam: '; diff --git a/library/phpmailer/language/phpmailer.lang-nl.php b/library/phpmailer/language/phpmailer.lang-nl.php index d2c380b09d..c86d7699bf 100644 --- a/library/phpmailer/language/phpmailer.lang-nl.php +++ b/library/phpmailer/language/phpmailer.lang-nl.php @@ -1,25 +1,24 @@ <?php /** -* PHPMailer language file: refer to English translation for definitive list -* Dutch Version +* PHPMailer language file: refer to class.phpmailer.php for definitive list. +* Dutch Version by Tuxion <team@tuxion.nl> */ -$PHPMAILER_LANG['authenticate'] = 'SMTP Fout: authenticatie mislukt.'; -$PHPMAILER_LANG['connect_host'] = 'SMTP Fout: Kon niet verbinden met SMTP host.'; -$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Fout: Data niet geaccepteerd.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; -$PHPMAILER_LANG['encoding'] = 'Onbekende codering: '; -$PHPMAILER_LANG['execute'] = 'Kon niet uitvoeren: '; -$PHPMAILER_LANG['file_access'] = 'Kreeg geen toegang tot bestand: '; -$PHPMAILER_LANG['file_open'] = 'Bestandsfout: Kon bestand niet openen: '; -$PHPMAILER_LANG['from_failed'] = 'De volgende afzender adressen zijn mislukt: '; -$PHPMAILER_LANG['instantiate'] = 'Kon mail functie niet initialiseren.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; -$PHPMAILER_LANG['provide_address'] = 'Er moet tenmiste één ontvanger emailadres opgegeven worden.'; -$PHPMAILER_LANG['mailer_not_supported'] = ' mailer wordt niet ondersteund.'; -$PHPMAILER_LANG['recipients_failed'] = 'SMTP Fout: De volgende ontvangers zijn mislukt: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['authenticate'] = 'SMTP-fout: authenticatie mislukt.';//SMTP Error: Could not authenticate. +$PHPMAILER_LANG['connect_host'] = 'SMTP-fout: kon niet verbinden met SMTP-host.';//SMTP Error: Could not connect to SMTP host. +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP-fout: data niet geaccepteerd.';//SMTP Error: Data not accepted. +$PHPMAILER_LANG['empty_message'] = 'Berichttekst is leeg';//Message body empty +$PHPMAILER_LANG['encoding'] = 'Onbekende codering: ';//Unknown encoding: +$PHPMAILER_LANG['execute'] = 'Kon niet uitvoeren: ';//Could not execute: +$PHPMAILER_LANG['file_access'] = 'Kreeg geen toegang tot bestand: ';//Could not access file: +$PHPMAILER_LANG['file_open'] = 'Bestandsfout: kon bestand niet openen: ';//File Error: Could not open file: +$PHPMAILER_LANG['from_failed'] = 'Het volgende afzendersadres is mislukt: ';//The following From address failed: +$PHPMAILER_LANG['instantiate'] = 'Kon mailfunctie niet initialiseren.';//Could not instantiate mail function. +$PHPMAILER_LANG['invalid_address'] = 'Ongeldig adres';//Invalid address +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer wordt niet ondersteund.';// mailer is not supported. +$PHPMAILER_LANG['provide_address'] = 'Er moet minstens één ontvanger worden opgegeven.';//You must provide at least one recipient email address. +$PHPMAILER_LANG['recipients_failed'] = 'SMTP-fout: de volgende ontvangers zijn mislukt: ';//SMTP Error: The following recipients failed: +$PHPMAILER_LANG['signing'] = 'Signeerfout: ';//Signing Error: +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Verbinding mislukt.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP-serverfout: ';//SMTP server error: +$PHPMAILER_LANG['variable_set'] = 'Kan de volgende variablen niet instellen of resetten: ';//Cannot set or reset variable: diff --git a/library/phpmailer/language/phpmailer.lang-no.php b/library/phpmailer/language/phpmailer.lang-no.php index 65cb884399..e6975203e3 100644 --- a/library/phpmailer/language/phpmailer.lang-no.php +++ b/library/phpmailer/language/phpmailer.lang-no.php @@ -7,19 +7,18 @@ $PHPMAILER_LANG['authenticate'] = 'SMTP Feil: Kunne ikke authentisere.'; $PHPMAILER_LANG['connect_host'] = 'SMTP Feil: Kunne ikke koble til SMTP host.'; $PHPMAILER_LANG['data_not_accepted'] = 'SMTP Feil: Data ble ikke akseptert.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; -$PHPMAILER_LANG['encoding'] = 'Ukjent encoding: '; +$PHPMAILER_LANG['empty_message'] = 'Meldingsinnholdet er tomt'; +$PHPMAILER_LANG['encoding'] = 'Ukjent tegnkoding: '; $PHPMAILER_LANG['execute'] = 'Kunne ikke utføre: '; -$PHPMAILER_LANG['file_access'] = 'Kunne ikke få tilgang til filen: '; +$PHPMAILER_LANG['file_access'] = 'Får ikke tilgang til filen: '; $PHPMAILER_LANG['file_open'] = 'Fil feil: Kunne ikke åpne filen: '; -$PHPMAILER_LANG['from_failed'] = 'Følgende Fra feilet: '; -$PHPMAILER_LANG['instantiate'] = 'Kunne ikke instantiate mail funksjonen.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; -$PHPMAILER_LANG['provide_address'] = 'Du må ha med minst en mottager adresse.'; +$PHPMAILER_LANG['from_failed'] = 'Følgende avsenderadresse feilet: '; +$PHPMAILER_LANG['instantiate'] = 'Kunne ikke initialisere mailfunksjonen.'; +$PHPMAILER_LANG['invalid_address'] = 'Meldingen ble ikke sendt, følgende adresse er ugyldig: '; +$PHPMAILER_LANG['provide_address'] = 'Du må angi minst en mottakeradresse.'; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer er ikke supportert.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP Feil: Følgende mottagere feilet: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['signing'] = 'Signeringsfeil: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() feilet.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP-serverfeil: '; +$PHPMAILER_LANG['variable_set'] = 'Kan ikke sette eller resette variabelen: ';
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-pl.php b/library/phpmailer/language/phpmailer.lang-pl.php index 6e9a82fda6..5a4ad38318 100644 --- a/library/phpmailer/language/phpmailer.lang-pl.php +++ b/library/phpmailer/language/phpmailer.lang-pl.php @@ -3,22 +3,22 @@ * PHPMailer language file: refer to English translation for definitive list * Polish Version */ -$PHPMAILER_LANG = array( - 'provide_address' => 'Należy podać prawidłowy adres email Odbiorcy.', - 'mailer_not_supported' => 'Wybrana metoda wysyłania wiadomości nie jest obsługiwana.', - 'execute' => 'Nie można uruchomić: ', - 'instantiate' => 'Nie można wywołać funkcji mail(). Sprawdź konfigurację serwera.', - 'authenticate' => 'Błąd SMTP: Nie można przeprowadzić uwierzytelniania.', - 'from_failed' => 'Następujący adres Nadawcy jest jest nieprawidłowy: ', - 'recipients_failed' => 'Błąd SMTP: Następujący odbiorcy są nieprawidłowi: ', - 'data_not_accepted' => 'Błąd SMTP: Dane nie zostały przyjęte.', - 'connect_host' => 'Błąd SMTP: Nie można połączyć się z wybranym hostem.', - 'file_access' => 'Brak dostępu do pliku: ', - 'file_open' => 'Nie można otworzyć pliku: ', - 'encoding' => 'Nieznany sposób kodowania znaków: ', - 'signing' => 'Błąd logowania: ', - 'smtp_error' => 'Błąd serwera SMTP: ', - 'empty_message' => 'Pusta treść wiadomości', - 'invalid_address' => 'Adres email jest nieprawidłowy', - 'variable_set' => 'Nie można ustawić lub zresetować zmiennej: ' -);
\ No newline at end of file + +$PHPMAILER_LANG['authenticate'] = 'Błąd SMTP: Nie można przeprowadzić autentykacji.'; +$PHPMAILER_LANG['connect_host'] = 'Błąd SMTP: Nie można połączyć się z wybranym hostem.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Błąd SMTP: Dane nie zostały przyjęte.'; +$PHPMAILER_LANG['empty_message'] = 'Wiadomość jest pusta.'; +$PHPMAILER_LANG['encoding'] = 'Nieznany sposób kodowania znaków: '; +$PHPMAILER_LANG['execute'] = 'Nie można uruchomić: '; +$PHPMAILER_LANG['file_access'] = 'Brak dostępu do pliku: '; +$PHPMAILER_LANG['file_open'] = 'Nie można otworzyć pliku: '; +$PHPMAILER_LANG['from_failed'] = 'Następujący adres Nadawcy jest nieprawidłowy: '; +$PHPMAILER_LANG['instantiate'] = 'Nie można wywołać funkcji mail(). Sprawdź konfigurację serwera.'; +$PHPMAILER_LANG['invalid_address'] = 'Nie można wysłać wiadomości, następujący adres Odbiorcy jest nieprawidłowy: '; +$PHPMAILER_LANG['provide_address'] = 'Należy podać prawidłowy adres email Odbiorcy.'; +$PHPMAILER_LANG['mailer_not_supported'] = 'Wybrana metoda wysyłki wiadomości nie jest obsługiwana.'; +$PHPMAILER_LANG['recipients_failed'] = 'Błąd SMTP: Następujący odbiorcy są nieprawidłowi: '; +$PHPMAILER_LANG['signing'] = 'Błąd podpisywania wiadomości: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() zakończone niepowodzeniem.'; +$PHPMAILER_LANG['smtp_error'] = 'Błąd SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Nie można zmienić zmiennej: '; diff --git a/library/phpmailer/language/phpmailer.lang-ro.php b/library/phpmailer/language/phpmailer.lang-ro.php index f6aa922556..1727cef04a 100644 --- a/library/phpmailer/language/phpmailer.lang-ro.php +++ b/library/phpmailer/language/phpmailer.lang-ro.php @@ -16,7 +16,7 @@ $PHPMAILER_LANG['file_access'] = 'Nu pot accesa fisierul: '; $PHPMAILER_LANG['file_open'] = 'Eroare de fisier: Nu pot deschide fisierul: '; $PHPMAILER_LANG['from_failed'] = 'Urmatoarele adrese From au dat eroare: '; $PHPMAILER_LANG['instantiate'] = 'Nu am putut instantia functia mail.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer nu este suportat.'; $PHPMAILER_LANG['provide_address'] = 'Trebuie sa adaugati cel putin un recipient (adresa de mail).'; $PHPMAILER_LANG['recipients_failed'] = 'Eroare SMTP: Urmatoarele adrese de mail au dat eroare: '; @@ -24,4 +24,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'Eroare SMTP: Urmatoarele adrese de ma //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-ru.php b/library/phpmailer/language/phpmailer.lang-ru.php index d6990525de..9e583dae05 100644 --- a/library/phpmailer/language/phpmailer.lang-ru.php +++ b/library/phpmailer/language/phpmailer.lang-ru.php @@ -7,19 +7,18 @@ $PHPMAILER_LANG['authenticate'] = 'Ошибка SMTP: ошибка авторизации.'; $PHPMAILER_LANG['connect_host'] = 'Ошибка SMTP: не удается подключиться к серверу SMTP.'; $PHPMAILER_LANG['data_not_accepted'] = 'Ошибка SMTP: данные не приняты.'; -//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; $PHPMAILER_LANG['encoding'] = 'Неизвестный вид кодировки: '; $PHPMAILER_LANG['execute'] = 'Невозможно выполнить команду: '; $PHPMAILER_LANG['file_access'] = 'Нет доступа к файлу: '; $PHPMAILER_LANG['file_open'] = 'Файловая ошибка: не удается открыть файл: '; $PHPMAILER_LANG['from_failed'] = 'Неверный адрес отправителя: '; $PHPMAILER_LANG['instantiate'] = 'Невозможно запустить функцию mail.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['provide_address'] = 'Пожалуйста, введите хотя бы один адрес e-mail получателя.'; $PHPMAILER_LANG['mailer_not_supported'] = ' - почтовый сервер не поддерживается.'; $PHPMAILER_LANG['recipients_failed'] = 'Ошибка SMTP: отправка по следующим адресам получателей не удалась: '; -//$PHPMAILER_LANG['signing'] = 'Signing Error: '; -//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; -//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; -//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['empty_message'] = 'Пустое тело сообщения'; +$PHPMAILER_LANG['invalid_address'] = 'Не отослано, неправильный формат email адреса: '; +$PHPMAILER_LANG['signing'] = 'Ошибка подписывания: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Ошибка соединения с SMTP-сервером'; +$PHPMAILER_LANG['smtp_error'] = 'Ошибка SMTP-сервера: '; +$PHPMAILER_LANG['variable_set'] = 'Невозможно установить или переустановить переменную: '; diff --git a/library/phpmailer/language/phpmailer.lang-se.php b/library/phpmailer/language/phpmailer.lang-se.php index 67e05f59c6..65d197995c 100644 --- a/library/phpmailer/language/phpmailer.lang-se.php +++ b/library/phpmailer/language/phpmailer.lang-se.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = 'Ingen åtkomst till fil: '; $PHPMAILER_LANG['file_open'] = 'Fil fel: Kunde inte öppna fil: '; $PHPMAILER_LANG['from_failed'] = 'Följande avsändaradress är felaktig: '; $PHPMAILER_LANG['instantiate'] = 'Kunde inte initiera e-postfunktion.'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['provide_address'] = 'Du måste ange minst en mottagares e-postadress.'; $PHPMAILER_LANG['mailer_not_supported'] = ' mailer stöds inte.'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP fel: Följande mottagare är felaktig: '; @@ -23,4 +23,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP fel: Följande mottagare är fel //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-sk.php b/library/phpmailer/language/phpmailer.lang-sk.php new file mode 100644 index 0000000000..1722a5f526 --- /dev/null +++ b/library/phpmailer/language/phpmailer.lang-sk.php @@ -0,0 +1,25 @@ +<?php +/** +* PHPMailer language file: refer to English translation for definitive list +* Slovak Version +* Author: Michal Tinka <michaltinka@gmail.com> +*/ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Error: Chyba autentifikácie.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Error: Nebolo možné nadviazať spojenie so SMTP serverom.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Error: Dáta neboli prijaté'; +$PHPMAILER_LANG['empty_message'] = 'Prázdne telo správy.'; +$PHPMAILER_LANG['encoding'] = 'Neznáme kódovanie: '; +$PHPMAILER_LANG['execute'] = 'Nedá sa vykonať: '; +$PHPMAILER_LANG['file_access'] = 'Súbor nebol nájdený: '; +$PHPMAILER_LANG['file_open'] = 'File Error: Súbor sa otvoriť pre čítanie: '; +$PHPMAILER_LANG['from_failed'] = 'Následujúca adresa From je nesprávna: '; +$PHPMAILER_LANG['instantiate'] = 'Nedá sa vytvoriť inštancia emailovej funkcie.'; +$PHPMAILER_LANG['invalid_address'] = 'Neodoslané, emailová adresa je nesprávna: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' emailový klient nieje podporovaný.'; +$PHPMAILER_LANG['provide_address'] = 'Musíte zadať aspoň jednu emailovú adresu príjemcu.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: Adresy príjemcov niesu správne '; +$PHPMAILER_LANG['signing'] = 'Chyba prihlasovania: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() zlyhalo.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP chyba serveru: '; +$PHPMAILER_LANG['variable_set'] = 'Nemožno nastaviť alebo resetovať premennú: '; diff --git a/library/phpmailer/language/phpmailer.lang-tr.php b/library/phpmailer/language/phpmailer.lang-tr.php index b066353f9e..abba0818d4 100644 --- a/library/phpmailer/language/phpmailer.lang-tr.php +++ b/library/phpmailer/language/phpmailer.lang-tr.php @@ -3,24 +3,24 @@ * PHPMailer language file: refer to English translation for definitive list * Turkish version * Türkçe Versiyonu -* Tercüme Adem GENÇ - tutastes@gmail.com +* ÝZYAZILIM - Elçin Özel - Can Yýlmaz - Mehmet Benlioðlu */ -$PHPMAILER_LANG['authenticate'] = 'SMTP Hatası: Kimlik doğrulanamıyor.'; -$PHPMAILER_LANG['connect_host'] = 'SMTP Hatası: SMTP hosta bağlanamıyor.'; +$PHPMAILER_LANG['authenticate'] = 'SMTP Hatası: Doğrulanamıyor.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Hatası: SMTP hosta bağlanılamıyor.'; $PHPMAILER_LANG['data_not_accepted'] = 'SMTP Hatası: Veri kabul edilmedi.'; $PHPMAILER_LANG['empty_message'] = 'Mesaj içeriği boş'; -$PHPMAILER_LANG['encoding'] = 'Bilinmeyen kodlama: '; -$PHPMAILER_LANG['execute'] = 'Çalıştırılamıyor: '; +$PHPMAILER_LANG['encoding'] = 'Bilinmeyen şifreleme: '; +$PHPMAILER_LANG['execute'] = 'Çalıtırılamıyor: '; $PHPMAILER_LANG['file_access'] = 'Dosyaya erişilemiyor: '; -$PHPMAILER_LANG['file_open'] = 'Dosya Hatası: Dosya açılamadı: '; -$PHPMAILER_LANG['from_failed'] = 'Aşağıdaki adresler başarısız oldu: '; -$PHPMAILER_LANG['instantiate'] = 'Email fonksiyonu kanıt sunulamadı.'; -$PHPMAILER_LANG['invalid_email'] = 'Geçersiz email adresi'; -$PHPMAILER_LANG['provide_address'] = 'En az bir alıcı email adresini vermeniz gerekmektedir.'; +$PHPMAILER_LANG['file_open'] = 'Dosya Hatası: Dosya açılamıyor: '; +$PHPMAILER_LANG['from_failed'] = 'Başarısız olan gönderici adresi: '; +$PHPMAILER_LANG['instantiate'] = 'Örnek mail fonksiyonu oluşturulamadı.'; +$PHPMAILER_LANG['invalid_address'] = 'Gönderilmedi, email adresi geçersiz: '; +$PHPMAILER_LANG['provide_address'] = 'En az bir tane mail adresi belirtmek zorundasınız alıcının email adresi.'; $PHPMAILER_LANG['mailer_not_supported'] = ' mailler desteklenmemektedir.'; -$PHPMAILER_LANG['recipients_failed'] = 'SMTP Hatası: Aşağıdaki alıcılara teslim edilemedi: '; -$PHPMAILER_LANG['signing'] = 'Hata İmzalama: '; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Hatası: alıcılara ulaımadı: '; +$PHPMAILER_LANG['signing'] = 'İmzalama hatası: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP bağlantı() başarısız.'; $PHPMAILER_LANG['smtp_error'] = 'SMTP sunucu hatası: '; -$PHPMAILER_LANG['variable_set'] = 'Ayarlayamıyor veya değişken sıfırlanamıyor: '; -?>
\ No newline at end of file +$PHPMAILER_LANG['variable_set'] = 'Ayarlanamıyor yada sıfırlanamıyor: '; diff --git a/library/phpmailer/language/phpmailer.lang-uk.php b/library/phpmailer/language/phpmailer.lang-uk.php new file mode 100644 index 0000000000..112bba8468 --- /dev/null +++ b/library/phpmailer/language/phpmailer.lang-uk.php @@ -0,0 +1,24 @@ +<?php +/** +* PHPMailer language file: refer to English translation for definitive list +* Ukrainian Version by Yuriy Rudyy <yrudyy@prs.net.ua> +*/ + +$PHPMAILER_LANG['authenticate'] = 'Помилка SMTP: помилка авторизації.'; +$PHPMAILER_LANG['connect_host'] = 'Помилка SMTP: не вдається підєднатися до серверу SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Помилка SMTP: дані не прийняті.'; +$PHPMAILER_LANG['encoding'] = 'Невідомий тип кодування: '; +$PHPMAILER_LANG['execute'] = 'Неможливо виконати команду: '; +$PHPMAILER_LANG['file_access'] = 'Немає доступу до файлу: '; +$PHPMAILER_LANG['file_open'] = 'Помилка файлової системи: не вдається відкрити файл: '; +$PHPMAILER_LANG['from_failed'] = 'Невірна адреса відправника: '; +$PHPMAILER_LANG['instantiate'] = 'Неможливо запустити функцію mail.'; +$PHPMAILER_LANG['provide_address'] = 'Будь-ласка, введіть хоча б одну адресу e-mail отримувача.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' - поштовий сервер не підтримується.'; +$PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: відправти наступним отрмувачам не вдалася: '; +$PHPMAILER_LANG['empty_message'] = 'Пусте тіло повідомлення'; +$PHPMAILER_LANG['invalid_address'] = 'Не відправлено, невірний формат email адреси: '; +$PHPMAILER_LANG['signing'] = 'Помилка підпису: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка зєднання із SMTP-сервером'; +$PHPMAILER_LANG['smtp_error'] = 'Помилка SMTP-сервера: '; +$PHPMAILER_LANG['variable_set'] = 'Неможливо встановити або перевстановити змінну: '; diff --git a/library/phpmailer/language/phpmailer.lang-zh.php b/library/phpmailer/language/phpmailer.lang-zh.php index fef66f8cb1..b0e8f4e091 100644 --- a/library/phpmailer/language/phpmailer.lang-zh.php +++ b/library/phpmailer/language/phpmailer.lang-zh.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_open'] = '文件錯誤:無法打開文件:'; $PHPMAILER_LANG['from_failed'] = '發送地址錯誤:'; $PHPMAILER_LANG['execute'] = '無法執行:'; $PHPMAILER_LANG['instantiate'] = '未知函數調用。'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['provide_address'] = '必須提供至少一個收件人地址。'; $PHPMAILER_LANG['mailer_not_supported'] = '發信客戶端不被支持。'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP 錯誤:收件人地址錯誤:'; @@ -23,4 +23,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP 錯誤:收件人地址錯誤:'; //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file diff --git a/library/phpmailer/language/phpmailer.lang-zh_cn.php b/library/phpmailer/language/phpmailer.lang-zh_cn.php index b188404359..78eb9acf36 100644 --- a/library/phpmailer/language/phpmailer.lang-zh_cn.php +++ b/library/phpmailer/language/phpmailer.lang-zh_cn.php @@ -15,7 +15,7 @@ $PHPMAILER_LANG['file_access'] = '无法访问文件:'; $PHPMAILER_LANG['file_open'] = '文件错误:无法打开文件:'; $PHPMAILER_LANG['from_failed'] = '发送地址错误:'; $PHPMAILER_LANG['instantiate'] = '未知函数调用。'; -//$PHPMAILER_LANG['invalid_email'] = 'Not sending, email address is invalid: '; +//$PHPMAILER_LANG['invalid_address'] = 'Not sending, email address is invalid: '; $PHPMAILER_LANG['mailer_not_supported'] = '发信客户端不被支持。'; $PHPMAILER_LANG['provide_address'] = '必须提供至少一个收件人地址。'; $PHPMAILER_LANG['recipients_failed'] = 'SMTP 错误:收件人地址错误:'; @@ -23,4 +23,3 @@ $PHPMAILER_LANG['recipients_failed'] = 'SMTP 错误:收件人地址错误:'; //$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; //$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; //$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; -?>
\ No newline at end of file |
