diff --git a/api/app/Helpers/TaxonomyHelper.php b/api/app/Helpers/TaxonomyHelper.php index 779ef43af5..a551756ed0 100644 --- a/api/app/Helpers/TaxonomyHelper.php +++ b/api/app/Helpers/TaxonomyHelper.php @@ -126,7 +126,19 @@ public static function parseScientificName($inStr, $conn = null, $rankId = 0, $k //cycles through the final terms to evaluate and extract infraspecific data while($sciStr = array_shift($sciNameArr)){ if($testArr = self::cleanInfra($sciStr)){ - self::setInfraNode($sciStr, $sciNameArr, $retArr, $authorArr, $testArr['infra']); + if($sciNameArr){ + $infraStr = array_shift($sciNameArr); + if(preg_match('/^[a-z]{3,}$/', $infraStr)){ + $retArr['unitind3'] = $testArr['infra']; + $retArr['unitname3'] = $infraStr; + unset($authorArr); + $authorArr = array(); + } + else{ + $authorArr[] = $sciStr; + $authorArr[] = $infraStr; + } + } } elseif($kingdomName == 'Animalia' && !$retArr['unitname3'] && ($rankId == 230 || preg_match('/^[a-z]{3,}$/',$sciStr) || preg_match('/^[A-Z]{3,}$/',$sciStr))){ $retArr['unitind3'] = ''; @@ -246,22 +258,6 @@ public static function cleanInfra($testStr){ return $retArr; } - private static function setInfraNode($sciStr, &$sciNameArr, &$retArr, &$authorArr, $rankTag){ - if($sciNameArr){ - $infraStr = array_shift($sciNameArr); - if(preg_match('/^[a-z]{3,}$/', $infraStr)){ - $retArr['unitind3'] = $rankTag; - $retArr['unitname3'] = $infraStr; - unset($authorArr); - $authorArr = array(); - } - else{ - $authorArr[] = $sciStr; - $authorArr[] = $infraStr; - } - } - } - //Taxonomic indexing functions public static function rebuildHierarchyEnumTree($conn = null){ $status = true; diff --git a/classes/DwcArchiverOccurrence.php b/classes/DwcArchiverOccurrence.php index abfd3edb39..864f23e698 100644 --- a/classes/DwcArchiverOccurrence.php +++ b/classes/DwcArchiverOccurrence.php @@ -498,11 +498,9 @@ public function getAssociationStr($occid, $associationType = null){ private function getAssociationJSON($occid) { // Build SQL to find any associations for the occurrence record passed with occid - $sql = 'SELECT occid, occidAssociate, relationship, subType, identifier,' . - 'basisOfRecord, resourceUrl, verbatimSciname, locationOnHost ' . - 'FROM omoccurassociations ' . - 'WHERE (occid = ' . $occid . ' OR occidAssociate = ' . $occid . ') '; - + $sql = 'SELECT occid, associationType, occidAssociate, relationship, subType, identifier, basisOfRecord, resourceUrl, verbatimSciname, locationOnHost + FROM omoccurassociations + WHERE (occid = ' . $occid . ' OR occidAssociate = ' . $occid . ') '; if ($rs = $this->conn->query($sql)) { // No associations, so just return an empty string, and quit the function diff --git a/classes/OccurrenceAssociations.php b/classes/OccurrenceAssociations.php index dc6dae3640..8be2e7c3f8 100644 --- a/classes/OccurrenceAssociations.php +++ b/classes/OccurrenceAssociations.php @@ -164,9 +164,9 @@ private function parseAssocSpecies($assocSpeciesStr,$occid){ private function databaseAssocSpecies($assocArr, $occid){ if($assocArr){ - $sql = 'INSERT INTO omoccurassociations(occid, verbatimsciname, relationship) VALUES'; + $sql = 'INSERT INTO omoccurassociations(occid, associationType, verbatimsciname, relationship) VALUES'; foreach($assocArr as $aStr){ - $sql .= '('.$occid.',"'.$this->conn->real_escape_string($aStr).'","associatedSpecies"), '; + $sql .= '('.$occid.', "observational", "'.$this->conn->real_escape_string($aStr).'","associatedSpecies"), '; } $sql = trim($sql,', '); //echo $sql; exit; diff --git a/classes/OccurrenceEditorManager.php b/classes/OccurrenceEditorManager.php index de328c594e..b8dde554d6 100644 --- a/classes/OccurrenceEditorManager.php +++ b/classes/OccurrenceEditorManager.php @@ -1016,7 +1016,7 @@ public function editOccurrence($postArr, $editorStatus){ if($postArr['host']) $sqlHost = 'UPDATE omoccurassociations SET verbatimsciname = "'.$postArr['host'].'" WHERE associd = '.$postArr['hostassocid'].' '; else $sqlHost = 'DELETE FROM omoccurassociations WHERE associd = '.$postArr['hostassocid'].' '; } - else $sqlHost = 'INSERT INTO omoccurassociations(occid,relationship,verbatimsciname) VALUES('.$this->occid.',"host","'.$postArr['host'].'")'; + else $sqlHost = 'INSERT INTO omoccurassociations(occid,associationType,relationship,verbatimsciname) VALUES('.$this->occid.',"observational","host","'.$postArr['host'].'")'; $this->conn->query($sqlHost); } //Update occurrence record @@ -1341,7 +1341,8 @@ public function addOccurrence($postArr){ } //Deal with host data if(array_key_exists('host',$postArr)){ - $sql = 'INSERT INTO omoccurassociations(occid,relationship,verbatimsciname) VALUES('.$this->occid.',"host","'.$this->cleanInStr($postArr['host']).'")'; + $sql = 'INSERT INTO omoccurassociations(occid, associationType, relationship, verbatimsciname) + VALUES('.$this->occid.', "observational", "host", "'.$this->cleanInStr($postArr['host']).'")'; if(!$this->conn->query($sql)){ $status .= '(WARNING adding host: '.$this->conn->error.') '; } @@ -1551,7 +1552,7 @@ public function deleteOccurrence($delOccid){ $archiveArr['dateDeleted'] = date('r').' by '.$USER_DISPLAY_NAME; $archiveObj = json_encode($archiveArr); $stage = 'Create Archive'; - $sqlArchive = 'INSERT INTO omoccurarchive(archiveobj, occid, catalogNumber, occurrenceID, recordID) '. + $sqlArchive = 'REPLACE INTO omoccurarchive(archiveobj, occid, catalogNumber, occurrenceID, recordID) '. 'VALUES ("'.$this->cleanInStr($this->encodeStrTargeted($archiveObj,'utf8',$CHARSET)).'", '.$delOccid.','. (isset($archiveArr['catalogNumber']) && $archiveArr['catalogNumber']?'"'.$this->cleanInStr($archiveArr['catalogNumber']).'"':'NULL').', '. (isset($archiveArr['occurrenceID']) && $archiveArr['occurrenceID']?'"'.$this->cleanInStr($archiveArr['occurrenceID']).'"':'NULL').', '. @@ -1620,10 +1621,14 @@ public function cloneOccurrence($postArr){ if($sourceOccid != $this->occid && !in_array($this->occid,$retArr)){ $retArr[$this->occid] = $this->occid; if(isset($postArr['assocrelation']) && $postArr['assocrelation']){ - $sql = 'INSERT INTO omoccurassociations(occid, occidAssociate, relationship,createdUid) '. - 'values('.$this->occid.','.$sourceOccid.',"'.$postArr['assocrelation'].'",'.$GLOBALS['SYMB_UID'].') '; - if(!$this->conn->query($sql)){ - $this->errorArr[] = $LANG['ERROR_ADDING_REL'].': '.$this->conn->error; + $sql = 'INSERT INTO omoccurassociations(occid, associationType, occidAssociate, relationship,createdUid) VALUES(?, "internalOccurrence", ?, ?, ?) '; + if($stmt = $this->conn->prepare($sql)){ + $stmt->bind_param('iisi', $this->occid, $sourceOccid, $postArr['assocrelation'], $GLOBALS['SYMB_UID']); + $stmt->execute(); + if($stmt->error){ + $this->errorArr[] = $LANG['ERROR_ADDING_REL'].': '.$this->conn->error; + } + $stmt->close(); } } if(isset($postArr['carryoverimages']) && $postArr['carryoverimages']){ diff --git a/classes/OccurrenceIndividual.php b/classes/OccurrenceIndividual.php index a6fffe9948..81db408692 100644 --- a/classes/OccurrenceIndividual.php +++ b/classes/OccurrenceIndividual.php @@ -413,14 +413,24 @@ private function setOccurrenceRelationships(){ } } if($relOccidArr){ - $sql = 'SELECT o.occid, o.sciname, - CONCAT_WS("-",IFNULL(o.institutioncode, c.institutioncode), IFNULL(o.collectioncode, c.collectioncode)) as collcode, IFNULL(o.catalogNumber, o.otherCatalogNumbers) as catnum + $sql = 'SELECT o.occid, o.sciname, IFNULL(o.institutioncode, c.institutioncode) as instCode, IFNULL(o.collectioncode, c.collectioncode) as collCode, o.catalogNumber, o.occurrenceID, o.recordID FROM omoccurrences o INNER JOIN omcollections c ON o.collid = c.collid WHERE o.occid IN(' . implode(',', array_keys($relOccidArr)) . ')'; $rs = $this->conn->query($sql); while($r = $rs->fetch_object()){ foreach($relOccidArr[$r->occid] as $targetAssocID){ - $this->occArr['relation'][$targetAssocID]['objectID'] = $r->collcode . ':' . $r->catnum; + $objectID = $r->catalogNumber; + if($objectID) { + if(strpos($objectID, $r->instCode) === false){ + //Append institution and collection code to catalogNumber, but only if it is not already included + $collCode = $r->instCode; + if($r->collCode) $collCode .= '-' . $r->collCode; + $objectID = $collCode . ':' . $r->catalogNumber; + } + } + elseif($r->occurrenceID) $objectID = $r->occurrenceID; + else $objectID = $r->recordID; + $this->occArr['relation'][$targetAssocID]['objectID'] = $objectID; $this->occArr['relation'][$targetAssocID]['sciname'] = $r->sciname; } } @@ -545,26 +555,16 @@ private function setSource(){ $displayStr = $this->occArr['catalognumber']; } elseif(strpos($iUrl,'--OTHERCATALOGNUMBERS--') !== false && $this->occArr['othercatalognumbers']){ - if(substr($this->occArr['othercatalognumbers'],0,1) == '{'){ - if($this->occArr['othercatalognumbers']){ - foreach($this->occArr['othercatalognumbers'] as $idKey => $idArr){ - if(!$displayStr || $idKey == 'NEON sampleID' || $idKey == 'NEON sampleCode (barcode)'){ - $displayStr = $idArr[0]; - if($idKey == 'NEON sampleCode (barcode)') $iUrl = str_replace('sampleTag','barcode',$iUrl); - $indUrl = str_replace('--OTHERCATALOGNUMBERS--',$idArr[0],$iUrl); - if($idKey == 'NEON sampleCode (barcode)') break; - } - } + foreach($this->occArr['othercatalognumbers'] as $idArr){ + $tagName = $idArr['name']; + $idValue = $idArr['value']; + if(!$displayStr || $tagName == 'NEON sampleID' || $tagName == 'NEON sampleCode (barcode)'){ + $displayStr = $tagName; + if($tagName == 'NEON sampleCode (barcode)') $iUrl = str_replace('sampleTag','barcode',$iUrl); + $indUrl = str_replace('--OTHERCATALOGNUMBERS--', $idValue, $iUrl); + if($tagName == 'NEON sampleCode (barcode)') break; } } - else{ - $ocn = str_replace($this->occArr['othercatalognumbers'], ',', ';'); - $ocnArr = explode(';',$ocn); - $ocnValue = trim(array_pop($ocnArr)); - if(stripos($ocnValue,':')) $ocnValue = trim(array_pop(explode(':',$ocnValue))); - $indUrl = str_replace('--OTHERCATALOGNUMBERS--',$ocnValue,$iUrl); - $displayStr = $ocnValue; - } } elseif(strpos($iUrl,'--OCCURRENCEID--') !== false && $this->occArr['occurrenceid']){ $indUrl = str_replace('--OCCURRENCEID--',$this->occArr['occurrenceid'],$iUrl); @@ -1251,6 +1251,15 @@ public function restoreRecord(){ } $rsAssoc->free(); foreach($recArr['assoc'] as $pk => $secArr){ + if(empty($secArr['associationType'])){ + if(!empty($secArr['occidAssociate'])) $secArr['associationType'] = 'internalOccurrence'; + elseif(!empty($secArr['resourceUrl'])){ + if(!empty($secArr['verbatimSciname'])) $secArr['associationType'] = 'externalOccurrence'; + else $secArr['associationType'] = 'resource'; + } + elseif(!empty($secArr['verbatimSciname'])) $secArr['associationType'] = 'observational'; + else $secArr['associationType'] = 'resource'; + } $sql1 = 'INSERT INTO omoccurassociations('; $sql2 = 'VALUES('; foreach($secArr as $f => $v){ diff --git a/classes/OccurrenceMapManager.php b/classes/OccurrenceMapManager.php index 3c2749400f..fc7be18356 100644 --- a/classes/OccurrenceMapManager.php +++ b/classes/OccurrenceMapManager.php @@ -59,7 +59,7 @@ public function getCoordinateMap($start, $limit){ 'o.othercatalognumbers, c.institutioncode, c.collectioncode, c.CollectionName '. 'FROM omoccurrences o INNER JOIN omcollections c ON o.collid = c.collid '; - $this->sqlWhere .= 'AND ts.taxauthid = 1 '; + $this->sqlWhere .= 'AND (ts.taxauthid = 1 OR ts.taxauthid IS NULL) '; $sql .= $this->getTableJoins($this->sqlWhere); @@ -68,7 +68,6 @@ public function getCoordinateMap($start, $limit){ if(is_numeric($start) && $limit){ $sql .= "LIMIT ".$start.",".$limit; } - //echo '//SQL: ' . $sql; $result = $this->conn->query($sql); $color = 'e69e67'; $occidArr = array(); diff --git a/classes/OmAssociations.php b/classes/OmAssociations.php index ccc3b3ecb7..b15bc79621 100644 --- a/classes/OmAssociations.php +++ b/classes/OmAssociations.php @@ -77,13 +77,13 @@ public function getAssociationArr($filter = null){ $rs = $this->conn->query($sql); while($r = $rs->fetch_object()){ $prefix = ''; - if(strpos($r->catalogNumber, $r->instCode) === false){ + if($r->catalogNumber && strpos($r->catalogNumber, $r->instCode) === false){ $prefix = $r->instCode; if($r->collCode) $prefix .= '-' . $r->collCode; $prefix .= ':'; } foreach($relOccidArr[$r->occid] as $targetAssocID){ - $retArr[$targetAssocID]['object-catalogNumber'] = $prefix . $r->catalogNumber; + $retArr[$targetAssocID]['object-catalogNumber'] = $prefix . ($r->catalogNumber ?? 'not defined'); $retArr[$targetAssocID]['verbatimSciname'] = $r->sciname; } } diff --git a/classes/OmDeterminations.php b/classes/OmDeterminations.php index 24f9dcc0a3..8ed9d58e00 100644 --- a/classes/OmDeterminations.php +++ b/classes/OmDeterminations.php @@ -13,8 +13,16 @@ class OmDeterminations extends Manager{ public function __construct($conn){ parent::__construct(null, 'write', $conn); + /* + $this->fieldMap = array('identifiedBy' => 's', 'identifiedByAgentID' => 'i', 'identifiedByID' => 's', 'dateIdentified' => 's', 'dateIdentifiedInterpreted' => 's', + 'higherClassification' => 's', 'family' => 's', 'sciname' => 's', 'verbatimIdentification' => 's', 'scientificNameAuthorship' => 's', 'tidInterpreted' => 'i', + 'identificationUncertain' => 'i', 'identificationQualifier' => 's', 'genus' => 's', 'specificEpithet' => 's', 'verbatimTaxonRank' => 's', 'taxonRank' => 's', + 'infraSpecificEpithet' => 's', 'isCurrent' => 'i', 'printQueue' => 'i', 'appliedStatus' => 'i', 'securityStatus' => 'i', 'securityStatusReason' => 's', + 'detType' => 's', 'identificationReferences' => 's', 'identificationRemarks' => 's', 'taxonRemarks' => 's', 'identificationVerificationStatus' => 's', + 'taxonConceptID' => 's', 'sourceIdentifier' => 's', 'sortSequence' => 'i', 'recordID' => 's', 'createdUid' => 'i', 'modifiedUid' => 'i', 'dateLastModified' => 's'); + */ $this->schemaMap = array('identifiedBy' => 's', 'dateIdentified' => 's', 'higherClassification' => 's', 'family' => 's', 'sciname' => 's', 'verbatimIdentification' => 's', - 'scientificNameAuthorship' => 's', 'identificationUncertain' => 'i', 'identificationQualifier' => 's', 'isCurrent' => 'i', 'printQueue' => 'i', 'appliedStatus' => 'i', + 'scientificNameAuthorship' => 's', 'identificationQualifier' => 's', 'isCurrent' => 'i', 'printQueue' => 'i', 'appliedStatus' => 'i', 'securityStatus' => 'i', 'securityStatusReason' => 's', 'detType' => 's', 'identificationReferences' => 's', 'identificationRemarks' => 's', 'taxonRemarks' => 's', 'identificationVerificationStatus' => 's', 'taxonConceptID' => 's', 'sourceIdentifier' => 's', 'sortSequence' => 'i'); } diff --git a/classes/SpecUploadBase.php b/classes/SpecUploadBase.php index 6ab1ce898b..87655b749a 100644 --- a/classes/SpecUploadBase.php +++ b/classes/SpecUploadBase.php @@ -1960,6 +1960,7 @@ protected function loadIdentificationRecord($recMap){ unset($recMap['taxonrank']); unset($recMap['infraspecificepithet']); //Try to get author, if it's not there + /* if(!array_key_exists('scientificnameauthorship',$recMap) || !$recMap['scientificnameauthorship']){ //Parse scientific name to see if it has author imbedded $parsedArr = OccurrenceUtilities::parseScientificName($recMap['sciname'],$this->conn); @@ -1969,6 +1970,7 @@ protected function loadIdentificationRecord($recMap){ $recMap['sciname'] = trim($parsedArr['unitname1'].' '.$parsedArr['unitname2'].' '.$parsedArr['unitind3'].' '.$parsedArr['unitname3']); } } + */ if(!isset($recMap['sciname']) || !$recMap['sciname']) return false; if(!isset($recMap['identifiedby'])) $recMap['identifiedby'] = ''; @@ -2540,10 +2542,7 @@ protected function encodeString($inStr){ } } else{ - if(mb_detect_encoding($inStr,'UTF-8,ISO-8859-1',true) == 'ISO-8859-1'){ - $retStr = utf8_encode($inStr); - //$retStr = iconv("ISO-8859-1//TRANSLIT","UTF-8",$inStr); - } + $retStr = mb_convert_encoding($inStr, 'UTF-8', mb_detect_encoding($inStr)); } } elseif($this->targetCharset == "ISO-8859-1"){ diff --git a/classes/TaxonomyEditorManager.php b/classes/TaxonomyEditorManager.php index 7725987248..203af28408 100644 --- a/classes/TaxonomyEditorManager.php +++ b/classes/TaxonomyEditorManager.php @@ -592,7 +592,7 @@ public function loadNewName($dataArr){ $sqlEnumTree = 'INSERT INTO taxaenumtree(tid,parenttid,taxauthid) '. 'SELECT '.$tid.' as tid, parenttid, taxauthid FROM taxaenumtree WHERE tid = '.$parTid; if($this->conn->query($sqlEnumTree)){ - $sqlEnumTree2 = 'INSERT INTO taxaenumtree(tid,parenttid,taxauthid) '. + $sqlEnumTree2 = 'INSERT IGNORE INTO taxaenumtree(tid,parenttid,taxauthid) '. 'VALUES ('.$tid.','.$parTid.','.$this->taxAuthId.')'; if(!$this->conn->query($sqlEnumTree2)){ echo (isset($this->langArr['WARNING_TAXAENUMTREE2'])?$this->langArr['WARNING_TAXAENUMTREE2']:'WARNING: Taxon loaded into taxa, but failed to populate taxaenumtree(2)').': '.$this->conn->error; diff --git a/classes/TaxonomyUpload.php b/classes/TaxonomyUpload.php index 442d2a1b69..93cc7ceeee 100644 --- a/classes/TaxonomyUpload.php +++ b/classes/TaxonomyUpload.php @@ -955,17 +955,19 @@ public function getSourceArr(){ $sourceArr = array(); if(($fh = fopen($this->uploadTargetPath.$this->uploadFileName,'r')) !== FALSE){ $headerArr = fgetcsv($fh); - if(substr($headerArr[0], 0, 3) == chr(hexdec('EF')).chr(hexdec('BB')).chr(hexdec('BF'))){ - //Remove UTF-8 BOM - $headerArr[0] = trim(substr($headerArr[0], 3), ' "'); - } - foreach($headerArr as $field){ - $fieldStr = trim($field); - if($fieldStr){ - $sourceArr[] = $fieldStr; + if($headerArr){ + if(substr($headerArr[0], 0, 3) == chr(hexdec('EF')).chr(hexdec('BB')).chr(hexdec('BF'))){ + //Remove UTF-8 BOM + $headerArr[0] = trim(substr($headerArr[0], 3), ' "'); } - else{ - break; + foreach($headerArr as $field){ + $fieldStr = trim($field); + if($fieldStr){ + $sourceArr[] = $fieldStr; + } + else{ + break; + } } } } diff --git a/classes/TaxonomyUtilities.php b/classes/TaxonomyUtilities.php index c8662e84e5..9803842ba9 100644 --- a/classes/TaxonomyUtilities.php +++ b/classes/TaxonomyUtilities.php @@ -42,7 +42,7 @@ public static function parseScientificName($inStr, $conn = null, $rankId = 0, $k } //Remove extra spaces $inStr = preg_replace('/\s\s+/',' ',$inStr); - + if(!$inStr) return $retArr; $sciNameArr = explode(' ',trim($inStr)); $okToCloseConn = true; if($conn !== null) $okToCloseConn = false; @@ -127,7 +127,19 @@ public static function parseScientificName($inStr, $conn = null, $rankId = 0, $k //cycles through the final terms to evaluate and extract infraspecific data while($sciStr = array_shift($sciNameArr)){ if($testArr = self::cleanInfra($sciStr)){ - self::setInfraNode($sciStr, $sciNameArr, $retArr, $authorArr, $testArr['infra']); + if($sciNameArr){ + $infraStr = array_shift($sciNameArr); + if(preg_match('/^[a-z]{3,}$/', $infraStr)){ + $retArr['unitind3'] = $testArr['infra']; + $retArr['unitname3'] = $infraStr; + unset($authorArr); + $authorArr = array(); + } + else{ + $authorArr[] = $sciStr; + $authorArr[] = $infraStr; + } + } } elseif($kingdomName == 'Animalia' && !$retArr['unitname3'] && ($rankId == 230 || preg_match('/^[a-z]{3,}$/',$sciStr) || preg_match('/^[A-Z]{3,}$/',$sciStr))){ $retArr['unitind3'] = ''; @@ -247,22 +259,6 @@ public static function cleanInfra($testStr){ return $retArr; } - private static function setInfraNode($sciStr, &$sciNameArr, &$retArr, &$authorArr, $rankTag){ - if($sciNameArr){ - $infraStr = array_shift($sciNameArr); - if(preg_match('/^[a-z]{3,}$/', $infraStr)){ - $retArr['unitind3'] = $rankTag; - $retArr['unitname3'] = $infraStr; - unset($authorArr); - $authorArr = array(); - } - else{ - $authorArr[] = $sciStr; - $authorArr[] = $infraStr; - } - } - } - //Taxonomic indexing functions public static function rebuildHierarchyEnumTree($conn = null){ $status = true; diff --git a/collections/admin/specuploadmap.php b/collections/admin/specuploadmap.php index a87ac75bcf..739793a36e 100644 --- a/collections/admin/specuploadmap.php +++ b/collections/admin/specuploadmap.php @@ -587,9 +587,9 @@ function pkChanged(selObj){ - - - + + +