From f5defa03dc45d11199f47702cc36d774b95fa2fc Mon Sep 17 00:00:00 2001 From: Edward Gilbert Date: Sun, 26 May 2024 15:31:14 -0400 Subject: [PATCH 1/2] Hotfix 2024 05 22 (#1374) * Hotfix 2024-05-22 - Checklist management: -- Adjust taxon merge function to accommodate PHP v8.1 fatal exceptions during foreign key conflicts within SQL update statements, which previously simply returned false value with a warning - Image processing: -- Bug resolution to avoid fatal error where an array function is incorrectly used on a string. Resolves issue https://github.com/BioKIC/Symbiota/issues/1366 - Occurrence Mapping: -- Fix bug interfering with the exclusion of protected occurrences within the mapping tools when user is not approved to view these records. Associated with issue: https://github.com/BioKIC/Symbiota/issues/1375 Co-authored-by: Greg Post --- classes/ChecklistVoucherManager.php | 18 +++-- classes/ImageLocalProcessor.php | 6 +- classes/OccurrenceMapManager.php | 121 +++++++++++++--------------- config/symbbase.php | 2 +- 4 files changed, 72 insertions(+), 75 deletions(-) diff --git a/classes/ChecklistVoucherManager.php b/classes/ChecklistVoucherManager.php index 695ea8bb2e..c0c82c503b 100644 --- a/classes/ChecklistVoucherManager.php +++ b/classes/ChecklistVoucherManager.php @@ -58,13 +58,15 @@ public function renameTaxon($targetTid, $rareLocality = ''){ $statusStr = false; if(is_numeric($targetTid)){ $clTaxaID = $this->getClTaxaID($this->tid); - $sql = 'UPDATE fmchklsttaxalink SET TID = '.$targetTid.' WHERE (clTaxaID = '.$clTaxaID.')'; + //First transfer taxa that + $sql = 'UPDATE IGNORE fmchklsttaxalink SET TID = '.$targetTid.' WHERE (clTaxaID = '.$clTaxaID.')'; if($this->conn->query($sql)){ $this->tid = $targetTid; $this->taxonName = ''; $statusStr = true; } - else{ + if(!$this->conn->affected_rows){ + //Transferred failed due to target name already exiting within checklist $sqlTarget = 'SELECT clTaxaID, Habitat, Abundance, Notes, internalnotes, source, Nativity FROM fmchklsttaxalink WHERE (tid = '.$targetTid.') AND (clid = '.$this->clid.')'; $rsTarget = $this->conn->query($sqlTarget); if($row = $rsTarget->fetch_object()){ @@ -93,12 +95,12 @@ public function renameTaxon($targetTid, $rareLocality = ''){ $sqlSourceCl = 'SELECT Habitat, Abundance, Notes, internalnotes, source, Nativity FROM fmchklsttaxalink WHERE (clTaxaID = '.$clTaxaID.')'; $rsSourceCl = $this->conn->query($sqlSourceCl); if($row = $rsSourceCl->fetch_object()){ - $habitatSource = $this->cleanInStr($row->Habitat); - $abundSource = $this->cleanInStr($row->Abundance); - $notesSource = $this->cleanInStr($row->Notes); - $internalNotesSource = $this->cleanInStr($row->internalnotes); - $sourceSource = $this->cleanInStr($row->source); - $nativeSource = $this->cleanInStr($row->Nativity); + $habitatSource = $row->Habitat; + $abundSource = $row->Abundance; + $notesSource = $row->Notes; + $internalNotesSource = $row->internalnotes; + $sourceSource = $row->source; + $nativeSource = $row->Nativity; } $rsSourceCl->free(); //Transfer source chklsttaxalink data to target record diff --git a/classes/ImageLocalProcessor.php b/classes/ImageLocalProcessor.php index 97a422f5e9..2eb4c9b1b9 100644 --- a/classes/ImageLocalProcessor.php +++ b/classes/ImageLocalProcessor.php @@ -998,17 +998,17 @@ private function databaseImage($imgArr){ } if($this->conn->query('DELETE FROM images WHERE imgid = '.$r->imgid)){ //Remove images - $urlPath = current(parse_url($r->url, PHP_URL_PATH)); + $urlPath = parse_url($r->url, PHP_URL_PATH); if($urlPath && strpos($urlPath, $this->imgUrlBase) === 0){ $wFile = str_replace($this->imgUrlBase,$this->targetPathBase,$urlPath); if(file_exists($wFile) && is_writable($wFile)) unlink($wFile); } - $urlTnPath = current(parse_url($r->thumbnailUrl, PHP_URL_PATH)); + $urlTnPath = parse_url($r->thumbnailUrl, PHP_URL_PATH); if($urlTnPath && strpos($urlTnPath, $this->imgUrlBase) === 0){ $wFile = str_replace($this->imgUrlBase,$this->targetPathBase,$urlTnPath); if(file_exists($wFile) && is_writable($wFile)) unlink($wFile); } - $urlLgPath = current(parse_url($r->originalUrl, PHP_URL_PATH)); + $urlLgPath = parse_url($r->originalUrl, PHP_URL_PATH); if($urlLgPath && strpos($urlLgPath, $this->imgUrlBase) === 0){ $wFile = str_replace($this->imgUrlBase,$this->targetPathBase,$urlLgPath); if(file_exists($wFile) && is_writable($wFile)) unlink($wFile); diff --git a/classes/OccurrenceMapManager.php b/classes/OccurrenceMapManager.php index 837b97feff..53dc2771da 100644 --- a/classes/OccurrenceMapManager.php +++ b/classes/OccurrenceMapManager.php @@ -63,31 +63,29 @@ public function getCoordinateMap($start, $limit){ if(is_numeric($start) && $limit){ $sql .= "LIMIT ".$start.",".$limit; } - //echo "
SQL: ".$sql."
"; exit; + //echo '//SQL: ' . $sql; $result = $this->conn->query($sql); $color = 'e69e67'; $occidArr = array(); while($row = $result->fetch_object()){ - if(($row->DecimalLongitude <= 180 && $row->DecimalLongitude >= -180) && ($row->DecimalLatitude <= 90 && $row->DecimalLatitude >= -90)){ - $occidArr[] = $row->occid; - $collName = $row->CollectionName; - $tidInterpreted = $this->htmlEntities($row->tidinterpreted); - $latLngStr = $row->DecimalLatitude.",".$row->DecimalLongitude; - $coordArr[$collName][$row->occid]["llStr"] = $latLngStr; - $coordArr[$collName][$row->occid]["collid"] = $this->htmlEntities($row->collid); - //$tidcode = strtolower(str_replace(" ", "",$tidInterpreted.$row->sciname)); - //$tidcode = preg_replace( "/[^A-Za-z0-9 ]/","",$tidcode); - //$coordArr[$collName][$occId]["ns"] = $this->htmlEntities($tidcode); - $coordArr[$collName][$row->occid]["tid"] = $tidInterpreted; - $coordArr[$collName][$row->occid]["fam"] = ($row->family?strtoupper($row->family):'undefined'); - $coordArr[$collName][$row->occid]["sn"] = $row->sciname; - $coordArr[$collName][$row->occid]["id"] = $this->htmlEntities($row->identifier); - //$coordArr[$collName][$occId]["icode"] = $this->htmlEntities($row->institutioncode); - //$coordArr[$collName][$occId]["ccode"] = $this->htmlEntities($row->collectioncode); - //$coordArr[$collName][$occId]["cn"] = $this->htmlEntities($row->catalognumber); - //$coordArr[$collName][$occId]["ocn"] = $this->htmlEntities($row->othercatalognumbers); - $coordArr[$collName]["c"] = $color; - } + $occidArr[] = $row->occid; + $collName = $row->CollectionName; + $tidInterpreted = $this->htmlEntities($row->tidinterpreted); + $latLngStr = $row->DecimalLatitude.",".$row->DecimalLongitude; + $coordArr[$collName][$row->occid]["llStr"] = $latLngStr; + $coordArr[$collName][$row->occid]["collid"] = $this->htmlEntities($row->collid); + //$tidcode = strtolower(str_replace(" ", "",$tidInterpreted.$row->sciname)); + //$tidcode = preg_replace( "/[^A-Za-z0-9 ]/","",$tidcode); + //$coordArr[$collName][$occId]["ns"] = $this->htmlEntities($tidcode); + $coordArr[$collName][$row->occid]["tid"] = $tidInterpreted; + $coordArr[$collName][$row->occid]["fam"] = ($row->family?strtoupper($row->family):'undefined'); + $coordArr[$collName][$row->occid]["sn"] = $row->sciname; + $coordArr[$collName][$row->occid]["id"] = $this->htmlEntities($row->identifier); + //$coordArr[$collName][$occId]["icode"] = $this->htmlEntities($row->institutioncode); + //$coordArr[$collName][$occId]["ccode"] = $this->htmlEntities($row->collectioncode); + //$coordArr[$collName][$occId]["cn"] = $this->htmlEntities($row->catalognumber); + //$coordArr[$collName][$occId]["ocn"] = $this->htmlEntities($row->othercatalognumbers); + $coordArr[$collName]["c"] = $color; } $statsManager->recordAccessEventByArr($occidArr, 'map'); if(array_key_exists('undefined',$coordArr)){ @@ -105,44 +103,41 @@ public function getMappingData($recLimit, $extraFieldArr = null){ $coordArr = array(); if($this->sqlWhere){ $statsManager = new OccurrenceAccessStats(); - $sql = 'SELECT DISTINCT o.occid, CONCAT_WS(" ",o.recordedby,IFNULL(o.recordnumber,o.eventdate)) AS collector, o.sciname, o.tidinterpreted, '. - 'o.decimallatitude, o.decimallongitude, o.catalognumber, o.othercatalognumbers, c.institutioncode, c.collectioncode, c.colltype '; + $sql = 'SELECT DISTINCT o.occid, CONCAT_WS(" ",o.recordedby,IFNULL(o.recordnumber,o.eventdate)) AS collector, o.sciname, o.tidinterpreted, + o.decimallatitude, o.decimallongitude, o.catalognumber, o.othercatalognumbers, c.institutioncode, c.collectioncode, c.colltype '; if(isset($extraFieldArr) && is_array($extraFieldArr)){ foreach($extraFieldArr as $fieldName){ - $sql .= ", o.".$fieldName." "; + $sql .= ', o.' . $fieldName . ' '; } } $sql .= 'FROM omoccurrences o INNER JOIN omcollections c ON o.collid = c.collid '; $sql .= $this->getTableJoins($this->sqlWhere); $sql .= $this->sqlWhere; - $sql .= 'AND (o.decimallatitude BETWEEN -90 AND 90) AND (o.decimallongitude BETWEEN -180 AND 180) '; if(is_numeric($start) && $recLimit && is_numeric($recLimit)) $sql .= "LIMIT ".$start.",".$recLimit; - //echo "
SQL: ".$sql."
"; + //echo '
SQL: ' . $sql . '
'; $rs = $this->conn->query($sql); $occidArr = array(); while($r = $rs->fetch_assoc()){ - if(($r['decimallongitude'] <= 180 && $r['decimallongitude'] >= -180) && ($r['decimallatitude'] <= 90 && $r['decimallatitude'] >= -90)){ - $sciname = $r['sciname']; - if(!$sciname) $sciname = 'undefined'; - $coordArr[$sciname][$r['occid']]['instcode'] = $r['institutioncode']; - if($r['collectioncode']) $coordArr[$sciname][$r['occid']]['collcode'] = $r['collectioncode']; - $collType = 'obs'; - if(stripos($r['colltype'],'specimen')) $collType = 'spec'; - $coordArr[$sciname][$r['occid']]['colltype'] = $collType; - if($r['catalognumber']) $coordArr[$sciname][$r['occid']]['catnum'] = $r['catalognumber']; - if($r['othercatalognumbers']) $coordArr[$sciname][$r['occid']]['ocatnum'] = $r['othercatalognumbers']; - if($r['tidinterpreted']) $coordArr[$sciname]['tid'] = $r['tidinterpreted']; - $coordArr[$sciname][$r['occid']]['collector'] = $r['collector']; - $coordArr[$sciname][$r['occid']]['lat'] = $r['decimallatitude']; - $coordArr[$sciname][$r['occid']]['lng'] = $r['decimallongitude']; - if(isset($extraFieldArr) && is_array($extraFieldArr)){ - reset($extraFieldArr); - foreach($extraFieldArr as $fieldName){ - if(isset($r[$fieldName])) $coordArr[$sciname][$r['occid']][$fieldName] = $r[$fieldName]; - } + $sciname = $r['sciname']; + if(!$sciname) $sciname = 'undefined'; + $coordArr[$sciname][$r['occid']]['instcode'] = $r['institutioncode']; + if($r['collectioncode']) $coordArr[$sciname][$r['occid']]['collcode'] = $r['collectioncode']; + $collType = 'obs'; + if(stripos($r['colltype'],'specimen')) $collType = 'spec'; + $coordArr[$sciname][$r['occid']]['colltype'] = $collType; + if($r['catalognumber']) $coordArr[$sciname][$r['occid']]['catnum'] = $r['catalognumber']; + if($r['othercatalognumbers']) $coordArr[$sciname][$r['occid']]['ocatnum'] = $r['othercatalognumbers']; + if($r['tidinterpreted']) $coordArr[$sciname]['tid'] = $r['tidinterpreted']; + $coordArr[$sciname][$r['occid']]['collector'] = $r['collector']; + $coordArr[$sciname][$r['occid']]['lat'] = $r['decimallatitude']; + $coordArr[$sciname][$r['occid']]['lng'] = $r['decimallongitude']; + if(isset($extraFieldArr) && is_array($extraFieldArr)){ + reset($extraFieldArr); + foreach($extraFieldArr as $fieldName){ + if(isset($r[$fieldName])) $coordArr[$sciname][$r['occid']][$fieldName] = $r[$fieldName]; } - $occidArr[] = $r['occid']; } + $occidArr[] = $r['occid']; } $rs->free(); $statsManager->recordAccessEventByArr($occidArr, 'map'); @@ -210,9 +205,9 @@ public function getRecordCnt(){ //SQL where functions private function setGeoSqlWhere(){ global $USER_RIGHTS; + $sqlWhere = $this->getSqlWhere(); + $sqlWhere .= ($sqlWhere?'AND ':'WHERE ').'((o.decimallatitude BETWEEN -90 AND 90) AND (o.decimallongitude BETWEEN -180 AND 180)) '; if($this->searchTermArr){ - $sqlWhere = $this->getSqlWhere(); - $sqlWhere .= ($sqlWhere?'AND ':'WHERE ').'(o.DecimalLatitude IS NOT NULL AND o.DecimalLongitude IS NOT NULL) '; if(array_key_exists('clid',$this->searchTermArr) && $this->searchTermArr['clid']){ if(isset($this->searchTermArr['cltype']) && $this->searchTermArr['cltype'] == 'all'){ $sqlWhere .= "AND (ST_Within(p.point,GeomFromText('".$this->getClFootprintWkt()." '))) "; @@ -224,22 +219,22 @@ private function setGeoSqlWhere(){ elseif(array_key_exists("polycoords",$this->searchTermArr)){ $sqlWhere .= "AND (ST_Within(p.point,GeomFromText('".$this->searchTermArr["polycoords"]." '))) "; } - //Check and exclude records with sensitive species protections - if(array_key_exists('SuperAdmin',$USER_RIGHTS) || array_key_exists('CollAdmin',$USER_RIGHTS) || array_key_exists('RareSppAdmin',$USER_RIGHTS) || array_key_exists('RareSppReadAll',$USER_RIGHTS)){ - //Is global rare species reader, thus do nothing to sql and grab all records - } - elseif(isset($USER_RIGHTS['RareSppReader']) || isset($USER_RIGHTS['CollEditor'])){ - $securityCollArr = array(); - if(isset($USER_RIGHTS['CollEditor'])) $securityCollArr = $USER_RIGHTS['CollEditor']; - if(isset($USER_RIGHTS['RareSppReader'])) $securityCollArr = array_unique(array_merge($securityCollArr, $USER_RIGHTS['RareSppReader'])); - $sqlWhere .= ' AND (o.CollId IN ('.implode(',',$securityCollArr).') OR (o.LocalitySecurity = 0 OR o.LocalitySecurity IS NULL)) '; - } - else{ - $sqlWhere .= ' AND (o.LocalitySecurity = 0 OR o.LocalitySecurity IS NULL) '; - } - $this->sqlWhere = $sqlWhere; - //echo '
sql: '.$this->sqlWhere.'
'; exit; } + //Check and exclude records with sensitive species protections + if(array_key_exists('SuperAdmin',$USER_RIGHTS) || array_key_exists('CollAdmin',$USER_RIGHTS) || array_key_exists('RareSppAdmin',$USER_RIGHTS) || array_key_exists('RareSppReadAll',$USER_RIGHTS)){ + //Is global rare species reader, thus do nothing to sql and grab all records + } + elseif(isset($USER_RIGHTS['RareSppReader']) || isset($USER_RIGHTS['CollEditor'])){ + $securityCollArr = array(); + if(isset($USER_RIGHTS['CollEditor'])) $securityCollArr = $USER_RIGHTS['CollEditor']; + if(isset($USER_RIGHTS['RareSppReader'])) $securityCollArr = array_unique(array_merge($securityCollArr, $USER_RIGHTS['RareSppReader'])); + $sqlWhere .= ' AND (o.CollId IN ('.implode(',',$securityCollArr).') OR (o.LocalitySecurity = 0 OR o.LocalitySecurity IS NULL)) '; + } + else{ + $sqlWhere .= ' AND (o.LocalitySecurity = 0 OR o.LocalitySecurity IS NULL) '; + } + $this->sqlWhere = $sqlWhere; + //echo '
sql: '.$this->sqlWhere.'
'; exit; } //Shape functions diff --git a/config/symbbase.php b/config/symbbase.php index 0330d17b4f..3098049297 100644 --- a/config/symbbase.php +++ b/config/symbbase.php @@ -2,7 +2,7 @@ header('X-Frame-Options: DENY'); header('Cache-control: private'); // IE 6 FIX date_default_timezone_set('America/Phoenix'); -$CODE_VERSION = '3.0.30'; +$CODE_VERSION = '3.0.31'; if(!isset($CLIENT_ROOT) && isset($clientRoot)) $CLIENT_ROOT = $clientRoot; if(substr($CLIENT_ROOT,-1) == '/') $CLIENT_ROOT = substr($CLIENT_ROOT,0,strlen($CLIENT_ROOT)-1); From 80532df45724b54e5d3853ecc1770baafe20d410 Mon Sep 17 00:00:00 2001 From: Edward Gilbert Date: Fri, 19 Jul 2024 11:12:01 -0400 Subject: [PATCH 2/2] Hotfix 2024 06 24 (#1527) * Hotfix 2024-06-24 - Image tools -- Return false when file size cannot be determined within getImgDim1 function - Glossary (resolves issue: https://github.com/BioKIC/Symbiota/issues/1526) -- SQL error causing term list display to fail when a taxon group was included in the search -- Change table linkages associated with taxon group condition to LEFT JOINs so that all terms are displayed, including those without linkages to **synonyms** --- classes/GlossaryManager.php | 8 ++++++-- classes/ImageShared.php | 1 + config/symbbase.php | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/classes/GlossaryManager.php b/classes/GlossaryManager.php index 7969a908a1..3b037a2f1f 100644 --- a/classes/GlossaryManager.php +++ b/classes/GlossaryManager.php @@ -62,11 +62,15 @@ public function getTermSearch($keyword, $language, $tid, $deepSearch = 1){ $sqlWhere .= ') '; } if($language) $sqlWhere .= 'AND (g.language = "'.$this->cleanInStr($language).'") '; - if($tid) $sqlWhere .= 'AND (t.tid = '.$tid.' OR t2.tid = '.$tid.') '; + if($tid) $sqlWhere .= 'AND (taxalink.tid = '.$tid.' OR taxalink2.tid = '.$tid.') '; $sql = 'SELECT DISTINCT g.glossid, g.term, g.definition, tl.relationshipType, g2.glossid as glossid2, g2.term AS term2, g2.definition AS def2 FROM glossary g LEFT JOIN glossarytermlink tl ON g.glossid = tl.glossgrpid LEFT JOIN glossary g2 ON tl.glossid = g2.glossid '; - if($tid) $sql .= 'INNER JOIN glossarytermlink tl ON g.glossid = tl.glossid INNER JOIN glossarytaxalink t ON tl.glossgrpid = t.glossid INNER JOIN glossarytaxalink t2 ON g.glossid = t2.glossid '; + if($tid){ + $sql .= 'LEFT JOIN glossarytermlink termlink ON g.glossid = termlink.glossid + LEFT JOIN glossarytaxalink taxalink ON termlink.glossgrpid = taxalink.glossid + LEFT JOIN glossarytaxalink taxalink2 ON g.glossid = taxalink2.glossid '; + } if($sqlWhere) $sql .= 'WHERE '.substr($sqlWhere, 3); if($rs = $this->conn->query($sql)){ while($r = $rs->fetch_object()){ diff --git a/classes/ImageShared.php b/classes/ImageShared.php index 4e74136dea..411e501cee 100644 --- a/classes/ImageShared.php +++ b/classes/ImageShared.php @@ -1081,6 +1081,7 @@ private static function getImgDim1($imgUrl) { $block_size = hexdec($block_size[1]); while(!feof($handle)) { $i += $block_size; + if(!$block_size) return false; $new_block .= fread($handle, $block_size); if(isset($new_block[$i]) && $new_block[$i]=="\xFF") { // New block detected, check for SOF marker diff --git a/config/symbbase.php b/config/symbbase.php index 3098049297..7fac978ab3 100644 --- a/config/symbbase.php +++ b/config/symbbase.php @@ -2,7 +2,7 @@ header('X-Frame-Options: DENY'); header('Cache-control: private'); // IE 6 FIX date_default_timezone_set('America/Phoenix'); -$CODE_VERSION = '3.0.31'; +$CODE_VERSION = '3.0.32'; if(!isset($CLIENT_ROOT) && isset($clientRoot)) $CLIENT_ROOT = $clientRoot; if(substr($CLIENT_ROOT,-1) == '/') $CLIENT_ROOT = substr($CLIENT_ROOT,0,strlen($CLIENT_ROOT)-1);