diff --git a/build.gradle b/build.gradle index 27b62cb9..cb46a6c5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - version "7.0.0" + version "7.1.0-SNAPSHOT" group "au.org.ala.plugins.grails" } diff --git a/grails-app/assets/javascripts/exploreYourArea.js b/grails-app/assets/javascripts/exploreYourArea.js index b1ab626c..9fd08148 100644 --- a/grails-app/assets/javascripts/exploreYourArea.js +++ b/grails-app/assets/javascripts/exploreYourArea.js @@ -171,7 +171,6 @@ function init() { // update map state from URL hash loadStateFromHash(encodedHash); } else { - //console.log("defaultParam not set, geolocating..."); attemptGeolocation(); } @@ -183,7 +182,6 @@ function init() { // catch the link for "View all records" $('#viewAllRecords').on("click", function(e) { e.preventDefault(); - //var params = "q=taxon_name:*|"+$('#latitude').val()+"|"+$('#longitude').val()+"|"+$('#radius').val(); var params = "q=*:*&lat="+$('#latitude').val()+"&lon="+$('#longitude').val()+"&radius="+$('#radius').val()+"&fq=spatiallyValid:true"; if (speciesGroup != "ALL_SPECIES") { params += "&fq=species_group:" + speciesGroup; @@ -192,10 +190,8 @@ function init() { }); // catch the link for "Download" - // ?searchParams=${sr?.urlParameters?.encodeAsURL()}&targetUri=${(request.forwardURI)}&totalRecords=${sr.totalRecords} $('#downloadData').on("click", function(e) { e.preventDefault(); - //var params = "q=taxon_name:*|"+$('#latitude').val()+"|"+$('#longitude').val()+"|"+$('#radius').val(); var params = "?q=*:*&lat="+$('#latitude').val()+"&lon="+$('#longitude').val()+"&radius="+$('#radius').val()+"&fq=spatiallyValid:true"; if (speciesGroup != "ALL_SPECIES") { params += "&fq=species_group:" + speciesGroup; @@ -266,12 +262,8 @@ function init() { }); } -//var proj900913 = new OpenLayers.Projection("EPSG:900913"); -//var proj4326 = new OpenLayers.Projection("EPSG:4326"); - // pointer fn function initialize() { - //loadMap(); loadLeafletMap(); loadGroups(); } @@ -299,7 +291,6 @@ function loadLeafletMap() { center: latLng, zoom: MAP_VAR.zoom, scrollWheelZoom: false - //layerControl: null }); updateMarkerPosition(latLng); @@ -365,7 +356,6 @@ function loadLeafletMap() { zIndex: -10 } - // console.log("circlProps", circlProps, latLng, radius); circle = L.circle(latLng, radius, circlProps).addTo(MAP_VAR.map); // detect click event and trigger record info popup @@ -386,9 +376,8 @@ function loadLeafletMap() { updateMarkerAddress('Drag ended'); updateMarkerPosition(newLatLng); geocodePosition(newLatLng); - //LoadTaxaGroupCounts(); loadGroups(); - //loadRecordsLayer(); + // adjust map view for new location MAP_VAR.map.setView(latLng, MAP_VAR.zoom); MAP_VAR.layerControl.removeLayer(marker); // prevent duplicate controls @@ -412,7 +401,6 @@ function geocodePosition(pos) { latLng: gLatLng }, function(responses) { if (responses && responses.length > 0) { - // console.log("geocoded position", responses[0]); var address = responses[0].formatted_address; updateMarkerAddress(address); // update the info window for marker icon @@ -437,13 +425,11 @@ function updateMarkerAddress(str) { * Update the lat & lon hidden input elements */ function updateMarkerPosition(latLng) { - // console.log("updateMarkerPosition", latLng, latLng.lat); var lat = latLng.lat.toFixed(coordinatePrecision); var lng = latLng.lng.toFixed(coordinatePrecision); // store values in hidden fields $('#latitude').val(lat); $('#longitude').val(lng); - //console.log("updating hash lat", lat, $('#latitude').val()); $('#dialog-confirm #rad').html(MAP_VAR.radius); MAP_VAR.query = "?q=*%3A*&lat=" + lat + "&lon=" + lng + "&radius=" + MAP_VAR.radius; } @@ -455,10 +441,8 @@ function loadRecordsLayer(retry) { if (!MAP_VAR.map && !retry) { // in case a callback calls this function before map has initialised setTimeout(function() {if (!points || points.length == 0) {loadRecordsLayer(true)}}, 2000); - //console.log('retry triggered'); return; } else if (!MAP_VAR.map) { - //console.log('retry failed'); return; } @@ -473,7 +457,6 @@ function loadRecordsLayer(retry) { } // URL for GeoJSON web service - //var geoJsonUrl = MAP_VAR.biocacheServiceUrl + "/geojson/radius-points"; var speciesGroupParam = "species_group:" + (speciesGroup == "ALL_SPECIES" ? "*" : speciesGroup); var alaParams = jQuery.param({ q: (taxon) ? "taxon_name:\"" + taxon + "\"" : "*:*", @@ -484,12 +467,10 @@ function loadRecordsLayer(retry) { speciesGroupParam ], qc: MAP_VAR.queryContext - //zoom: (map && map.getZoom()) ? map.getZoom() : 12 }, true); // records popups need to know the species group MAP_VAR.removeFqs = "&fq=species_group:" + (speciesGroup === "ALL_SPECIES" ? "*" : speciesGroup) + "&fq=taxon_name:" + (taxon ? ("\"" + taxon + "\""): "*"); - // console.log("alaParams = ", alaParams, speciesGroupParam); var alaMapUrl = MAP_VAR.biocacheServiceUrl + "/ogc/wms/reflect?" + alaParams; var wmsParams = { @@ -501,13 +482,10 @@ function loadRecordsLayer(retry) { outline:"false", GRIDDETAIL: 32, // 64 || 32 ENV: "color:DF4A21;name:circle;size:4;opacity:0.7", - //ENV: "colormode:grid;name:square;size:3;opacity:0.7", uppercase: true }; - //console.log('About to call $.get', map); // JQuery AJAX call - //$.getJSON(alaMaprUrl, params, loadNewGeoJsonData); alaWmsLayer = L.tileLayer.wms(alaMapUrl, wmsParams).addTo(MAP_VAR.map); MAP_VAR.layerControl.addOverlay(alaWmsLayer, 'Records'); @@ -523,13 +501,8 @@ function loadRecordsLayer(retry) { function attemptGeolocation() { // HTML5 GeoLocation if (navigator && navigator.geolocation) { - // console.log("trying to get coords with navigator.geolocation...");   function getMyPostion(position) { - //alert('coords: '+position.coords.latitude+','+position.coords.longitude); - // console.log('geolocation "navigator" request accepted'); - //$('#mapCanvas').empty(); updateMarkerPosition(L.latLng(position.coords.latitude, position.coords.longitude)); - //LoadTaxaGroupCounts(); initialize(); } @@ -545,25 +518,15 @@ function attemptGeolocation() { initialize(); } - // Add message to browser - FF needs this as it is not easy to see - //var msg = 'Waiting for confirmation to use your current location (see browser message at top of window)'+ - // '
Click here to load map'; - //$('#mapCanvas').html(msg).css('color','red').css('font-size','14px'); - //map.remove; - //map = null; navigator.geolocation.getCurrentPosition(getMyPostion, positionWasDeclined); - //console.log("line after navigator.geolocation.getCurrentPosition...");   + // Neither functions gets called for some reason, so I've added a delay to initalize map anyway setTimeout(function() {if (!MAP_VAR.map) positionWasDeclined();}, 9000); } else if (google.loader && google.loader.ClientLocation) { // Google AJAX API fallback GeoLocation - // console.log("getting coords using google geolocation", google.loader.ClientLocation); updateMarkerPosition(L.latLng(google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude)); - //LoadTaxaGroupCounts(); initialize(); } else { - //alert("Client geolocation failed"); - //geocodeAddress(); MAP_VAR.zoom = 12; initialize(); } @@ -581,14 +544,12 @@ function geocodeAddress(reverseGeocode) { var parts = address.split(","); var lat = magellan(parts[0].trim()).latitude(); //.toDD(); var lng = magellan(parts[1].trim()).longitude(); //.toDD(); - //console.log("magellan", parts, lat, lng); if (lat && lng) { latLng = L.latLng(lat.toDD(), lng.toDD()); updateMarkerAddress("GPS coordinates: " + lat.toDD() + ", " + lng.toDD()); updateMarkerPosition(latLng); // reload map pin, etc - //console.log("geocodeAddress() calling loadRecordsLayer()"); initialize(); loadRecordsLayer(); } @@ -596,21 +557,16 @@ function geocodeAddress(reverseGeocode) { } if (!latLng && geocoder && address) { - //geocoder.getLocations(address, addAddressToPage); - // console.log("geocodeAddress with address string"); geocoder.geocode( {'address': address, region: MAP_VAR.geocodeRegion}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { // geocode was successful - //console.log('geocodeAddress results', results); updateMarkerAddress(results[0].formatted_address); var gLatLng = results[0].geometry.location; - // console.log("gLatLng", gLatLng.lat(), gLatLng.lng()); var latLng = L.latLng(gLatLng.lat(), gLatLng.lng()); updateMarkerPosition(latLng); // reload map pin, etc initialize(); loadRecordsLayer(); - //LoadTaxaGroupCounts(); } else { alert("Geocode was not successful for the following reason: " + status); } @@ -624,7 +580,6 @@ function geocodeAddress(reverseGeocode) { * Geocode location via Google Maps API */ function addAddressToPage(response) { - //map.clearOverlays(); if (!response || response.Status.code != 200) { alert("Sorry, we were unable to geocode that address"); } else { @@ -640,17 +595,13 @@ function addAddressToPage(response) { var speciesJson var globalSortOrder var globalOffset -var currentGroup var dataRequest +var lastParameters /** * Species group was clicked */ function groupClicked(el) { - if (dataRequest) { - dataRequest.abort(); - } - speciesJson = [] // Change the global var speciesGroup @@ -681,11 +632,26 @@ function groupClicked(el) { pageSize: -1 }; + // clone params and speciesGroup + var allParameters = $.extend({speciesGroup: speciesGroup}, params) + + // debounce to fix location.hash trigger issue + if (lastParameters && JSON.stringify(allParameters) === JSON.stringify(lastParameters)) { + return + } else { + lastParameters = allParameters + } + $('#rightList tbody').empty(); $(".scrollContent").scrollTop(0); $('#spinnerRow').show(); $("div#rightList").data("sort", sortField); // save 'sort' value to the DOM + var currentGroup = speciesGroup + + if (dataRequest) { + dataRequest.abort(); + } dataRequest = $.getJSON(uri, params, function(data) { $('#spinnerRow').hide(); @@ -696,17 +662,19 @@ function groupClicked(el) { sortSpeciesJson() // process JSON data from request - if (data) processSpeciesJsonData(data); + if (data) processSpeciesJsonData(data, currentGroup); }); } /** * Process the JSON data from an Species list AJAX request (species in area) */ -function processSpeciesJsonData(data) { +function processSpeciesJsonData(data, currentGroup) { var offset = globalOffset var pageSize = 50; + var contents = "" + // process JSON data if (data.length > 0) { var lastRow = $('#rightList tbody tr').length; @@ -746,7 +714,7 @@ function processSpeciesJsonData(data) { // add number of records tr = tr + '' + data[i].count + ' '; // write list item to page - $('#rightList tbody').append(tr); + contents += tr } $('#loadMoreSpecies').remove(); @@ -754,17 +722,24 @@ function processSpeciesJsonData(data) { if (offset + pageSize < data.length) { // add load more link var sortOrder = $("div#rightList").data("sort") ? $("div#rightList").data("sort") : "index"; - $('#rightList tbody').append(' Show more species'); + contents += ' Show more species'; } globalOffset += pageSize } else { // no spceies were found (either via paging or clicking on taxon group var text = '[no species found]'; - $('#rightList tbody').append(text); + contents += text; } + // only add to page if the group has not changed + if (currentGroup !== undefined && currentGroup !== speciesGroup) { + return + } + + $('#rightList tbody').append(contents); + // Register clicks for the list of species links so that map changes $('#rightList tbody tr').unbind('click.specieslink') $('#rightList tbody tr').bind('click.specieslink', function(e) { @@ -851,7 +826,6 @@ function sortSpeciesJson() { function loadGroups() { var url = MAP_VAR.biocacheServiceUrl +"/explore/groups"; var params = { - //"group": $(this).attr('title'), lat: $('#latitude').val(), lon: $('#longitude').val(), radius: $('#radius').val(), @@ -890,14 +864,13 @@ function populateSpeciesGroups(data) { if (group == "ALL_SPECIES") label = "all.species"; var rc = (group == speciesGroup) ? " class='activeRow'" : ""; // highlight active group var i18nLabel = jQuery.i18n.prop(label); - // console.log("i18n check", label, i18nLabel); + var h = ""+i18nLabel+""+count+""; $("#taxa-level-0 tbody").append(h); } } function bookmarkedSearch(lat, lng, zoom1, group) { - // console.log("bookmarkedSearch", lat, lng, zoom1, group); MAP_VAR.radius = radiusForZoom[zoom1]; // set global var MAP_VAR.zoom = parseInt(zoom1); $('select#radius').val(MAP_VAR.radius); // update drop-down widget @@ -915,7 +888,8 @@ function loadStateFromHash(encodedHash) { if (hashParts.length == 3) { bookmarkedSearch(hashParts[0], hashParts[1], hashParts[2], null); } else if (hashParts.length == 4) { - bookmarkedSearch(hashParts[0], hashParts[1], hashParts[2], hashParts[3]); + // not sure what is going on with the selected species group encoding, but this works + bookmarkedSearch(hashParts[0], hashParts[1], hashParts[2], decodeURIComponent(decodeURIComponent(hashParts[3]))); } else { attemptGeolocation(); } diff --git a/grails-app/conf/plugin.groovy b/grails-app/conf/plugin.groovy index 943263aa..5900a96a 100644 --- a/grails-app/conf/plugin.groovy +++ b/grails-app/conf/plugin.groovy @@ -147,6 +147,10 @@ alwaysshow.imagetab = false facets.defaultSelected = "data_resource_uid,taxon_name,year,multimedia" +// User properties are stored by "userdetails" or "biocache". +// This is required when using AWS Cognito with userdetails (more or less). +userproperties.provider="userdetails" + mapdownloads { baseLayers { default_layer { diff --git a/grails-app/services/au/org/ala/biocache/hubs/UserDataService.groovy b/grails-app/services/au/org/ala/biocache/hubs/UserDataService.groovy index d0ed2fa6..f235d6a0 100644 --- a/grails-app/services/au/org/ala/biocache/hubs/UserDataService.groovy +++ b/grails-app/services/au/org/ala/biocache/hubs/UserDataService.groovy @@ -16,22 +16,36 @@ package au.org.ala.biocache.hubs import grails.converters.JSON +import org.apache.http.entity.ContentType class UserDataService { def grailsApplication def webService + final static String USERDETAILS = 'userdetails' + final static String BIOCACHE = 'biocache' + def get(userId, type) { def data = [:] - if (userId && grailsApplication.config.userdetails.baseUrl) { + if (userId) { try { - def resp = webService.get(grailsApplication.config.getProperty('userdetails.baseUrl') + '/property/getProperty' + - "?alaId=${userId}&name=${URLEncoder.encode(grailsApplication.config.getProperty('info.app.name') +'.'+ type, "UTF-8")}") + String url; + if (USERDETAILS.equals(grailsApplication.config.getProperty("userproperties.provider")) && grailsApplication.config.userdetails.baseUrl) { + url = grailsApplication.config.getProperty('userdetails.baseUrl') + '/property/getProperty'; + } else if (BIOCACHE.equals(grailsApplication.config.getProperty("userproperties.provider")) && grailsApplication.config.biocache.baseUrl) { + url = grailsApplication.config.getProperty('biocache.baseUrl') + '/user/property' + } + if (url) { + def resp = webService.get( url + "?alaId=${userId}&name=${URLEncoder.encode(grailsApplication.config.getProperty('info.app.name') + '.' + type, "UTF-8")}", [:], ContentType.APPLICATION_JSON, true, false) - if (resp?.resp && resp?.resp[0]?.value && resp?.resp[0]?.value) { - data = JSON.parse(resp?.resp[0]?.value) + if (resp?.resp && resp?.resp[0]?.value && resp?.resp[0]?.value) { + data = JSON.parse(resp?.resp[0]?.value) + } else if (resp?.resp) { + // for the new format + data = JSON.parse(resp?.resp.values().first()) + } } } catch (err) { //fail with only a log entry @@ -44,11 +58,19 @@ class UserDataService { // return value indicates if set succeeds boolean set(userId, type, data) { - if (userId && grailsApplication.config.getProperty('userdetails.baseUrl')) { - def response = webService.post(grailsApplication.config.getProperty('userdetails.baseUrl') + '/property/saveProperty', null, - [alaId: userId, name: grailsApplication.config.getProperty('info.app.name') +'.'+ type, value: (data as JSON).toString()]) - - return response?.statusCode == 200 + if (userId) { + String url; + if (USERDETAILS.equals(grailsApplication.config.getProperty("userproperties.provider")) && grailsApplication.config.userdetails.baseUrl) { + url = grailsApplication.config.getProperty('userdetails.baseUrl') + '/property/saveProperty'; + } else if (BIOCACHE.equals(grailsApplication.config.getProperty("userproperties.provider")) && grailsApplication.config.biocache.baseUrl) { + url = grailsApplication.config.getProperty('biocache.baseUrl') + '/user/property' + } + if (url) { + def response = webService.post(url, null, + [alaId: userId, name: grailsApplication.config.getProperty('info.app.name') + '.' + type, value: (data as JSON).toString()], + ContentType.APPLICATION_JSON, true, false) + return response?.statusCode == 200 + } } return false } diff --git a/grails-app/taglib/au/org/ala/biocache/hubs/OccurrenceTagLib.groovy b/grails-app/taglib/au/org/ala/biocache/hubs/OccurrenceTagLib.groovy index 9c342009..f3de0715 100644 --- a/grails-app/taglib/au/org/ala/biocache/hubs/OccurrenceTagLib.groovy +++ b/grails-app/taglib/au/org/ala/biocache/hubs/OccurrenceTagLib.groovy @@ -658,7 +658,7 @@ class OccurrenceTagLib { url = "${userName}" } else if (dataResourceUid == grailsApplication.config.getProperty('dataResourceUuid.iNaturalist', String,'')) { // iNaturalist - url = "${userName}" + url = "${userName}" } else if (dataResourceUid == grailsApplication.config.getProperty('dataResourceUuid.flickr', String,'') && occurrenceId) { // Flickr // Munge occurrenceId to get the URL, as we don't have the user-name stored in biocache in order to generate it @@ -702,13 +702,13 @@ class OccurrenceTagLib { def tagBody if (cr.processed && cr.raw && cr.processed == cr.raw) { - tagBody = cr.processed + tagBody = pipeWhitespace(cr.processed) } else if (!cr.raw && cr.processed) { - tagBody = cr.processed + tagBody = pipeWhitespace(cr.processed) } else if (cr.raw && !cr.processed) { - tagBody = cr.raw + tagBody = pipeWhitespace(cr.raw) } else { - tagBody = "${cr.processed}
${alatag.message(code:"alatag.supplied.as")} ${cr.raw}" + tagBody = "${pipeWhitespace(cr.processed)}
${alatag.message(code:"alatag.supplied.as")} ${cr.raw}" } output += occurrenceTableRow(annotate:"true", section:"dataset", fieldCode:"${key}", fieldName:"${label}") { tagBody @@ -718,6 +718,11 @@ class OccurrenceTagLib { out << output } + def pipeWhitespace(str) { + // inject whitespace around pipe + str.replaceAll("(?<=\\S)\\|(?=\\S)", " | ") + } + def formatDynamicLabel(str){ if(str){ str.substring(0, str.length() - 2).replaceAll("_", " ")