diff --git a/build.gradle b/build.gradle index 3e5d5c198..a9b99c8cf 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } } -version "3.0.1" +version "3.0.2" group "au.org.ala.plugins.grails" apply plugin:"eclipse" @@ -82,7 +82,7 @@ dependencies { //compile 'org.grails.plugins:cache-ehcache:3.0.0.M1' compile 'org.grails.plugins:http-builder-helper:1.0.2.ALA' compile "org.grails.plugins:ala-admin-plugin:2.2", noCache - compile "org.grails.plugins:ala-auth:3.2.2", noCache + compile "org.grails.plugins:ala-auth:3.2.3", noCache if(!inplace) { compile "au.org.ala.plugins.grails:images-client-plugin:1.2", noCache diff --git a/grails-app/assets/javascripts/exploreYourArea.js b/grails-app/assets/javascripts/exploreYourArea.js index 08f25659b..b9d382d41 100644 --- a/grails-app/assets/javascripts/exploreYourArea.js +++ b/grails-app/assets/javascripts/exploreYourArea.js @@ -633,6 +633,7 @@ function groupClicked(el) { radius: $('#radius').val(), fq: "geospatial_kosher:true", qc: MAP_VAR.queryContext, + sort: "count", pageSize: 50 }; //var params = "?latitude=${latitude}&longitude=${longitude}&radius=${radius}&taxa="+taxa+"&rank="+rank; @@ -659,21 +660,23 @@ function processSpeciesJsonData(data, appendResults) { var infoTitle = "view species page"; var recsTitle = "view list of records"; // iterate over list of species from search - for (i=0;i'; - // add scientific name - tr = tr + ''+data[i].name+''; // add common name if (data[i].commonName) { - tr = tr + ' : ' + data[i].commonName+''; + tr = tr + '' + data[i].commonName +''; + } else { + tr = tr + ' [Not supplied] '; } + // add scientific name + tr = tr + ''+data[i].name+''; + // add links to species page and occurrence search (inside hidden div) if(MAP_VAR.speciesPageUrl) { - var speciesInfo = '
'; if (data[i].guid) { speciesInfo = speciesInfo + ' 0; + + for (var i = 0; i < profiles.length; i++) { + if (profiles[i] === userPref.dataProfile) { + userProfileEnabled = true; + break; + } + } + } + + var profileSelect = $('#prefer_profile'); + + if (userPref.disableAll) { // if disable all + profileSelect.val('disableall-option'); + } else if (userProfileEnabled && userPref.dataProfile !== null) { // if a profile selected and enabled + profileSelect.val(userPref.dataProfile); + } else { // if no profile selected or selected profile disabled, use system default + profileSelect.val(prefSettings.data('defaultprofilename')); + } + + $('#profile_expand').val(userPref.expand ? 'expanded' : 'collapsed'); + }) + + // when submit the user preference dlg + $("#submitPref :input.submit").on("click", function(e) { + e.preventDefault(); + var prefSettings = $('#DQPrefSettings'); + var userPref = prefSettings.data('userpref-json'); + + // check user preferred profile + var prefProfile = $('#prefer_profile').val(); + if (prefProfile === 'disableall-option') { + userPref.disableAll = true; + userPref.dataProfile = null; + } else { + userPref.disableAll = false; + userPref.dataProfile = prefProfile; + } + + // set expand + userPref.expand = $('#profile_expand').val() === 'expanded'; + $.cookie.json = true; + // if user logged in + if (BC_CONF.userId) { + // save the dq profile detail expand/collapse state + $.cookie(BC_CONF.expandKey, {expand: userPref.expand}); + $.ajax({ + url: BC_CONF.serverName + "/user/" + BC_CONF.prefKey, + type: "POST", + contentType: 'application/json', + data: JSON.stringify(userPref), + success: applyUserPreference, // reload on success + error: function() { + window.alert(jQuery.i18n.prop('dq.warning.failedtosave')); + } + }).always(function() { + $('#DQPrefSettings').modal('hide'); + }) + } else { // else save in cookie + $.cookie(BC_CONF.prefKey, userPref, { expires: 365 }); + // save the dq profile detail expand/collapse state + $.cookie(BC_CONF.expandKey, {expand: userPref.expand}); + $('#DQPrefSettings').modal('hide'); + applyUserPreference(userPref) + } + }) + + function applyUserPreference(userPref) { + var prefSettings = $('#DQPrefSettings'); + + // enabled filters of current profile, used to remove expanded fqs in url + var filtersvalue = prefSettings.data('filters'); + var filterSet = new Set(); + for (var i = 0; i < filtersvalue.length; i++) { + filterSet.add(filtersvalue[i]); + } + + // get current url + var url = $(location).attr('href'); + + // 1. remove qualityProfile from URL + url = removeFromURL(url, "qualityProfile=", false); + // 2. remove disable all from URL + url = removeFromURL(url, "disableAllQualityFilters=", false); + // 3. remove disableQualityFilter from URL + var disabledQualityFilters = $.url().param('disableQualityFilter'); + if (disabledQualityFilters !== undefined) { + // if only 1 category disabled + if (typeof disabledQualityFilters === "string") { + url = removeFromURL(url, "disableQualityFilter=", false); + } else { + for (var i = 0; i < disabledQualityFilters.length; i++) { + url = removeFromURL(url, "disableQualityFilter=", false); + } + } + } + + // fqs contains current fqs in url, it could be expanded or user specified + var fqList = $.url().param('fq'); + var fqs = []; + if (fqList !== undefined) { + if (typeof fqList === "object") { + for (var i = 0; i < fqList.length; i++) { + fqs.push(fqList[i]); + } + } else if (typeof fqList === "string") { + fqs.push(fqList); + } + } + + // 4. remove fqs from URL + for (var i = 0; i < fqs.length; i++) { + // remove those belong to this profile so only user specified fqs stay + if (filterSet.has(fqs[i])) { + url = removeFromURL(url, 'fq=' + encodeURIComponent(fqs[i]).replace(/%20/g, "+").replace(/[()]/g, escape), true); + } + } + + // 5. add qualityProfile=xxx or disableAllQualityFilter=true to URL + if (userPref.disableAll) { + url = prependURL(url,"disableAllQualityFilters=true", true); + } else { + url = prependURL(url, "qualityProfile=" + encodeURIComponent(userPref.dataProfile).replace(/%20/g, "+").replace(/[()]/g, escape), true); + } + + window.location.href = url; + } + // form validation for form#facetRefineForm $("#submitFacets :input.submit").on("click", function(e) { e.preventDefault(); @@ -674,13 +824,18 @@ $(document).ready(function() { // switch caret style $('.dq-filters-collapse').click(function (e) { + $.cookie.json = true; var el = $(this).find('i'); if ($(el).hasClass('fa-caret-right')) { $(el).removeClass('fa-caret-right'); $(el).addClass('fa-caret-down'); + // save the expand/collapse state to cookie so when page refresh + // we can restore the state + $.cookie(BC_CONF.expandKey, {expand: true}); } else if ($(el).hasClass('fa-caret-down')) { $(el).removeClass('fa-caret-down'); $(el).addClass('fa-caret-right'); + $.cookie(BC_CONF.expandKey, {expand: false}); } }); @@ -838,9 +993,10 @@ $(document).ready(function() { // get current url var url = $(location).attr('href'); - var fitlers = $("form#filterRefineForm").find("td.filternames"); - var filterStatus = $("form#filterRefineForm").find(":input.filters"); - var expanded = $("form#filterRefineForm").find(".expanded"); + var filterForm = $("form#filterRefineForm") + var fitlers = filterForm.find("td.filternames"); + var filterStatus = filterForm.find(":input.filters"); + var expanded = filterForm.find(".expanded"); // replace url encoded %20 with '+' because groovy encodes space to '+' $.each(filterStatus, function( i, status ) { @@ -870,6 +1026,9 @@ $(document).ready(function() { } }) + // profile in URL may be invalid, replace it with the actual profile being used + url = replaceInvalidProfile(url, $.url().param('qualityProfile'), filterForm.attr('data-profile')) + window.location.href = url; }) @@ -914,6 +1073,43 @@ $(document).ready(function() { } } + function prependURL(url, queryParamsToAppend, afterSearchKey) { + var anchorpos = url.indexOf("#"); + var ancchorpart = ''; + if (anchorpos !== -1) { + ancchorpart = url.substring(anchorpos); + url = url.substring(0, anchorpos); + } + + var queryStringpos = url.indexOf('?'); + var queryString = '' + if (queryStringpos !== -1) { + queryString = url.substring(queryStringpos + 1); + url = url.substring(0, queryStringpos); + } + + // trim query string + queryString = queryString.trim(); + var queries = [] + if (queryString !== '') { + queries = queryString.split('&'); + } + + var idx = 0; + // if insert it after search keys: q=, qid=, lat=, lng=, radius= + if (afterSearchKey) { + for (; idx < queries.length; idx++) { + var query = queries[idx]; + if (!query.startsWith('q=') && !query.startsWith('qid=') && !query.startsWith('lat=') && !query.startsWith('lng=') && !query.startsWith('radius=') && !query.startsWith('taxa=')) { + break; + } + } + } + + queries.splice(idx, 0, queryParamsToAppend) + return url + '?' + queries.join('&') + ancchorpart; + } + function removeFromURL(url, sToRemove, exactMatch) { var anchorpos = url.indexOf('#'); var anchorpart = ""; @@ -976,6 +1172,18 @@ $(document).ready(function() { } }); + // user preference settings and download link tooltips will be above the control + $("#usersettings, a.newDownload").qtip({ + style: { + classes: 'ui-tooltip-rounded ui-tooltip-shadow' + }, + position: { + target: 'mouse', + my: 'bottom center', + adjust: { x: -6, y: -10 } + } + }); + // maultiple facets popup - sortable column heading links $("a.fsort").on("click", function(e) { e.preventDefault(); @@ -1152,6 +1360,25 @@ $(document).ready(function() { } $('#modal-dismiss-dq').modal() + + // expand / collapse data profile details + var dqFilterCollapse = $('#dq-filters-collapse') + dqFilterCollapse.collapse({ + toggle: BC_CONF.expandProfileDetails + }) + + switchCaretStyle($('.dq-filters-collapse')); + + function switchCaretStyle(elem) { + var el = elem.find('i'); + if (elem.hasClass('collapsed')) { + $(el).removeClass('fa-caret-down'); + $(el).addClass('fa-caret-right'); + } else { + $(el).removeClass('fa-caret-right'); + $(el).addClass('fa-caret-down'); + } + } }); // end JQuery document ready /** diff --git a/grails-app/assets/stylesheets/exploreYourArea.css b/grails-app/assets/stylesheets/exploreYourArea.css index cca08608f..64961160c 100644 --- a/grails-app/assets/stylesheets/exploreYourArea.css +++ b/grails-app/assets/stylesheets/exploreYourArea.css @@ -132,7 +132,8 @@ table#cellCountsLegend { width: 5%; } #rightList .sciName { - width: 85%; + text-align: left; + width: 50%; } #rightList .rightCounts { padding-right: 5px; diff --git a/grails-app/conf/plugin.groovy b/grails-app/conf/plugin.groovy index 3446b61da..b8f9a5bee 100644 --- a/grails-app/conf/plugin.groovy +++ b/grails-app/conf/plugin.groovy @@ -30,8 +30,10 @@ auth.admin_role = "ROLE_ADMIN" serverName = 'http://dev.ala.org.au:8080' dataquality.enabled = false -dataquality.baseUrl = 'https://biocache.ala.org.au/data-quality/' +dataquality.baseUrl = 'https://data-quality-service-test.ala.org.au' dataquality.recordCountCacheSpec = 'expireAfterWrite=1d' +dataquality.prefkey = 'dqUserProfile' +dataquality.expandKey = 'dqDetailExpand' // skin settings organisation.baseUrl = "https://www.ala.org.au" diff --git a/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy b/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy index 74c84f63a..6741348a8 100644 --- a/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy +++ b/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy @@ -19,11 +19,14 @@ import au.org.ala.dataquality.model.QualityProfile import au.org.ala.web.CASRoles import com.maxmind.geoip2.record.Location import grails.converters.JSON +import groovy.json.JsonSlurper import groovy.util.logging.Slf4j import org.grails.web.json.JSONArray import org.grails.web.json.JSONElement import org.grails.web.json.JSONObject +import org.springframework.beans.factory.annotation.Value +import javax.servlet.http.HttpServletRequest import java.text.SimpleDateFormat import static au.org.ala.biocache.hubs.TimingUtils.time @@ -33,12 +36,14 @@ import static au.org.ala.biocache.hubs.TimingUtils.time */ @Slf4j class OccurrenceController { - def webServicesService, facetsCacheService, postProcessingService, authService + def webServicesService, facetsCacheService, postProcessingService, authService, qualityService, userService def SESSION_NAVIGATION_DTO = "SESSION_NAVIGATION_DTO" + @Value('${dataquality.enabled}') + boolean dataQualityEnabled + GeoIpService geoIpService - QualityService qualityService def ENVIRO_LAYER = "el" def CONTEXT_LAYER = "cl" @@ -62,9 +67,10 @@ class OccurrenceController { def list(SpatialSearchRequestParams requestParams) { def start = System.currentTimeMillis() - def activeProfile = time("active profile") { qualityService.activeProfile(requestParams.qualityProfile) } - - normaliseRequestParams(requestParams) + // get the user preference settings + def userPref = userService.getUserPref(authService?.getUserId(), request) + // apply the user preference settings + normaliseRequestParams(requestParams, userPref) try { //the configured grouping @@ -159,18 +165,6 @@ class OccurrenceController { hasImages = true } - def qualityCategories = time("quality categories") { qualityService.findAllEnabledCategories(requestParams.qualityProfile) } - def qualityFiltersByLabel = time("quality filters by label") { qualityService.getEnabledFiltersByLabel(requestParams.qualityProfile) } - def qualityTotalCount = time("quality total count") { qualityService.countTotalRecords(requestParams) } - def groupedEnabledFilters = time("get grouped enabled filters") { qualityService.getGroupedEnabledFilters(requestParams.qualityProfile) } - def qualityFilterDescriptionsByLabel = groupedEnabledFilters.collectEntries {[(it.key) : it.value*.description.join(' and ')] } - - def (fqInteract, dqInteract, UserFQColors, DQColors) = time("process user fq interactions") { postProcessingService.processUserFQInteraction(requestParams, searchResults?.activeFacetObj) } - - def messagePropertiesFile = time("message properties file") { webServicesService.getMessagesPropertiesFile() } - def assertionCodeMap = time("assertionCodeMap") { webServicesService.getAssertionCodeMap() } - def translatedFilterMap = postProcessingService.translateValues(groupedEnabledFilters, messagePropertiesFile, assertionCodeMap) - log.debug "defaultFacets = ${defaultFacets}" OccurrenceNavigationDTO navigationDTO = new OccurrenceNavigationDTO(); @@ -183,18 +177,14 @@ class OccurrenceController { navigationDTO.setSearchRequestResultSize(searchResults.totalRecords); request.getSession().setAttribute(SESSION_NAVIGATION_DTO, navigationDTO); - def inverseFilters = time("inverseFilters") { qualityService.getAllInverseCategoryFiltersForProfile(activeProfile) } - - def processingTime = (System.currentTimeMillis() - start) - log.info ("Timing - list processing time: {} ms", processingTime) - [ + def resultData = + [ sr: searchResults, searchRequestParams: requestParams, defaultFacets: defaultFacets, groupedFacets: groupedFacets, groupedFacetsMap: groupedFacetsMap, dynamicFacets: dynamicFacets, - translatedFilterMap: translatedFilterMap, selectedDataResource: getSelectedResource(requestParams.q), hasImages: hasImages, showSpeciesImages: false, @@ -204,20 +194,55 @@ class OccurrenceController { userId: authService?.getUserId(), userEmail: authService?.getEmail(), processingTime: (System.currentTimeMillis() - start), - wsTime: wsTime, - qualityCategories: qualityCategories, - qualityFiltersByLabel: qualityFiltersByLabel, - qualityTotalCount: qualityTotalCount, - fqInteract: fqInteract, - qualityFilterDescriptionsByLabel: qualityFilterDescriptionsByLabel, - dqInteract: dqInteract, - UserFQColors: UserFQColors, - DQColors: DQColors, - activeProfile: activeProfile, - qualityProfiles: time("findAllEnabledProfiles") { qualityService.findAllEnabledProfiles(true) }, - inverseFilters: inverseFilters - ] + wsTime: wsTime + ] + + if (dataQualityEnabled) { + def qualityCategories = time("quality categories") { qualityService.findAllEnabledCategories(requestParams.qualityProfile) } + def qualityFiltersByLabel = [:] + def groupedEnabledFilters = [:] + def qualityFilterDescriptionsByLabel = [:] + def translatedFilterMap = [:] + def interactionMap = [:] + // if disable all quality filters, we don't need to retrieve them, it saves time + if (!requestParams.disableAllQualityFilters) { + qualityFiltersByLabel = time("quality filters by label") { qualityService.getEnabledFiltersByLabel(requestParams.qualityProfile) } + groupedEnabledFilters = time("get grouped enabled filters") { qualityService.getGroupedEnabledFilters(requestParams.qualityProfile) } + qualityFilterDescriptionsByLabel = groupedEnabledFilters.collectEntries {[(it.key) : it.value*.description.join(' and ')] } + interactionMap = time("process user fq interactions") { postProcessingService.processUserFQInteraction(requestParams, searchResults?.activeFacetObj) } + + def messagePropertiesFile = time("message properties file") { webServicesService.getMessagesPropertiesFile() } + def assertionCodeMap = time("assertionCodeMap") { webServicesService.getAssertionCodeMap() } + translatedFilterMap = postProcessingService.translateValues(groupedEnabledFilters, messagePropertiesFile, assertionCodeMap) + } + def qualityTotalCount = time("quality total count") { qualityService.countTotalRecords(requestParams) } + + def activeProfile = time("active profile") { qualityService.activeProfile(requestParams.qualityProfile) } + def inverseFilters = time("inverseFilters") { qualityService.getAllInverseCategoryFiltersForProfile(activeProfile) } + + // params.qualityProfile is used to construct exclude count link, we need to set with the actual profile being used + params.qualityProfile = activeProfile?.shortName + resultData.translatedFilterMap = translatedFilterMap + resultData.qualityCategories = qualityCategories + resultData.qualityFiltersByLabel = qualityFiltersByLabel + resultData.qualityTotalCount = qualityTotalCount + resultData.fqInteract = interactionMap.fqInteract ?: [:] + resultData.qualityFilterDescriptionsByLabel = qualityFilterDescriptionsByLabel + resultData.dqInteract = interactionMap.dqInteract ?: [:] + resultData.UserFQColors = interactionMap.UserFQColors ?: [:] + resultData.DQColors = interactionMap.DQColors ?: [:] + resultData.activeProfile = activeProfile + resultData.defaultProfileName = qualityService.activeProfile()?.shortName + resultData.expandProfileDetails = getProfileDetailExpandState(userPref, request) + resultData.userPref = userPref + resultData.qualityProfiles = time("findAllEnabledProfiles") { qualityService.findAllEnabledProfiles(true) } + resultData.inverseFilters = inverseFilters + } + + def processingTime = (System.currentTimeMillis() - start) + log.info ("Timing - list processing time: {} ms", processingTime) + resultData } catch (Exception ex) { log.warn "Error getting search results: $ex.message", ex flash.message = "${ex.message}" @@ -229,7 +254,7 @@ class OccurrenceController { * Massage the request params with defaults and processing additional query params * @param requestParams The request params to normalise */ - private void normaliseRequestParams(SpatialSearchRequestParams requestParams) { + private void normaliseRequestParams(SpatialSearchRequestParams requestParams, userPref = null) { requestParams.fq = params.list("fq") as String[] // override Grails binding which splits on internal commas in value log.debug "requestParams = ${requestParams}" @@ -255,6 +280,42 @@ class OccurrenceController { if (!requestParams.q) { requestParams.q = "*:*" } + + + if (!requestParams.disableAllQualityFilters && userPref?.size() > 0) { + // find all enabled profiles, this list will be used for both requested profile and user default profile + def enabledProfiles = qualityService.findAllEnabledProfiles(true) + def requestProfileValid = requestParams.qualityProfile && enabledProfiles?.any { it.shortName == requestParams.qualityProfile } + + // if no valid profile setting, see if we can get any preference settings + if (!requestProfileValid) { + // if user disables all + if (userPref.disableAll == true) { + requestParams.disableAllQualityFilters = true + } else { + // apply user preferred profile then system default + def profileToUse = '' + def profileToUseFullName = '' + + def userPrefProfileValid = userPref.dataProfile && enabledProfiles?.any { it.shortName == userPref.dataProfile } + if (userPrefProfileValid) { + profileToUse = userPref.dataProfile + profileToUseFullName = enabledProfiles?.find { it.shortName == userPref.dataProfile }?.name + } else { + def systemDefaultProfile = qualityService.activeProfile() + profileToUse = systemDefaultProfile?.shortName + profileToUseFullName = systemDefaultProfile.name + } + + // profile not null then it's disabled or not-exist + if (requestParams.qualityProfile) { + // to display full name of the profile being used + flash.message = "The selected profile ${requestParams.qualityProfile} is not available, ${profileToUseFullName} is currently applied.".toString() + } + requestParams.qualityProfile = profileToUse + } + } + } } def dataQualityExcludeCounts(SpatialSearchRequestParams requestParams) { @@ -624,4 +685,24 @@ class OccurrenceController { data.count = qualityService.getExcludeCount(params.categoryLabel, profile.getCategories(), requestParams) render data as JSON } + + // profile details expand/collapse will be kept for whole session + private def getProfileDetailExpandState(userPref, HttpServletRequest request) { + def expandKey = "${grailsApplication.config.dataquality.expandKey}" + def rawCookie = PostProcessingService.getCookieValue(request.getCookies(), expandKey, null) + + // if already have expand/collapse settings, use it to overwrite user preference + if (rawCookie != null) { + try { + // apply session settings + def val = new JsonSlurper().parseText(URLDecoder.decode(rawCookie, "UTF-8")) + return val.expand + } catch (UnsupportedEncodingException ex) { + log.error(ex.getMessage(), ex) + } + } + // rawCookie == null means the start of session so use default user settings + + return userPref.expand + } } diff --git a/grails-app/controllers/au/org/ala/biocache/hubs/UserController.groovy b/grails-app/controllers/au/org/ala/biocache/hubs/UserController.groovy index 1de51e975..daa6225d4 100644 --- a/grails-app/controllers/au/org/ala/biocache/hubs/UserController.groovy +++ b/grails-app/controllers/au/org/ala/biocache/hubs/UserController.groovy @@ -26,18 +26,22 @@ class UserController { def authService def userDataService - def set(String type) { + // return true if set succeeds, return false otherwise + boolean set(String type) { def userId = authService.getUserId() def data = request.JSON if (!userId) { render status: 403 - } else if (data && userId) { - userDataService.set(userId, type, data) - render status: 200 - } else { + } else if (!data) { render status: 404 } + + if (userDataService.set(userId, type, data)) { + render data as JSON + } else { + render status: 500 + } } def get(String type) { diff --git a/grails-app/i18n/messages_ca.properties b/grails-app/i18n/messages_ca.properties index 76e788618..204693303 100644 --- a/grails-app/i18n/messages_ca.properties +++ b/grails-app/i18n/messages_ca.properties @@ -227,15 +227,15 @@ list.facetcheckboxes.navigator02=Cap list.facetcheckboxes.button.updatefacetoptions=Actualitzar list.facetcheckboxes.button.resetfacetoptions=Restaurar els valors predeterminats list.facetcheckboxes.div02.title=Facetes personalitzades -list.resultsretuened.span.returnedtext=resultats per a -list.resultsretuened.button01=Esborrar totes -list.resultsretuened.navigator01=marcador de posici\u00f3 -list.resultsretuened.div01.des01=Els resultats de la cerca inclouen registres de sin\u00f2nims i taxons fills de -list.resultsretuened.div01.des02=marcador de posici\u00f3 -list.resultsretuened.div01.des03=enlla\u00e7 marcador de posici\u00f3 -list.resultsretuened.form.des01=El conjunt de resultats cont\u00e9 registres proporcionats sota els seg\u00fcents noms -list.resultsretuened.form.button01=Refinar la cerca -list.resultsretuened.form.div01=Llista de marcadors de posici\u00f3 del t\u00e0xon +list.resultsreturned.span.returnedtext=resultats per a +list.resultsreturned.button01=Esborrar totes +list.resultsreturned.navigator01=marcador de posici\u00f3 +list.resultsreturned.div01.des01=Els resultats de la cerca inclouen registres de sin\u00f2nims i taxons fills de +list.resultsreturned.div01.des02=marcador de posici\u00f3 +list.resultsreturned.div01.des03=enlla\u00e7 marcador de posici\u00f3 +list.resultsreturned.form.des01=El conjunt de resultats cont\u00e9 registres proporcionats sota els seg\u00fcents noms +list.resultsreturned.form.button01=Refinar la cerca +list.resultsreturned.form.div01=Llista de marcadors de posici\u00f3 del t\u00e0xon list.alert.title=Alertes de correu electr\u00f2nic list.alert.navigator01=Rep alertes de correu electr\u00f2nic per registres nous list.alert.navigator02=Rep alertes de correu electr\u00f2nic per anotacions noves @@ -1003,4 +1003,4 @@ dataResource=Font de dades dataProvider=Soci de dades dataResourceName=Font de dades dataProviderName=Soci de dades -list.taxa.notfound=48/5000\nNota\: no s''ha trobat cap coincid\u00e8ncia en el nom de t\u00e0xon per a {0} \ No newline at end of file +list.taxa.notfound=48/5000\nNota\: no s''ha trobat cap coincid\u00e8ncia en el nom de t\u00e0xon per a {0} diff --git a/grails-app/i18n/messages_de.properties b/grails-app/i18n/messages_de.properties index feba17934..036145104 100644 --- a/grails-app/i18n/messages_de.properties +++ b/grails-app/i18n/messages_de.properties @@ -226,15 +226,15 @@ list.facetcheckboxes.navigator02=Keine list.facetcheckboxes.button.updatefacetoptions=Aktualisieren list.facetcheckboxes.button.resetfacetoptions=Auf Grundeinstellungen zur\u00fccksetzen list.facetcheckboxes.div02.title=Benutzerdefinierte Attribute -list.resultsretuened.span.returnedtext=Ergebnisse f\u00fcr -list.resultsretuened.button01=Alles l\u00f6schen -list.resultsretuened.navigator01=Platzhalter -list.resultsretuened.div01.des01=Die Suchergebnisse beinhalten Eintr\u00e4ge f\u00fcr Synonyme und Tochtertaxa von -list.resultsretuened.div01.des02=Platzhalter -list.resultsretuened.div01.des03=Platzhalter-Link -list.resultsretuened.form.des01=Die Ergebnisse enthalten Datens\u00e4tze, die unter den folgenden Namen \u00fcbermittelt wurden -list.resultsretuened.form.button01=Suche verfeinern -list.resultsretuened.form.div01=Platzhalter Taxaliste +list.resultsreturned.span.returnedtext=Ergebnisse f\u00fcr +list.resultsreturned.button01=Alles l\u00f6schen +list.resultsreturned.navigator01=Platzhalter +list.resultsreturned.div01.des01=Die Suchergebnisse beinhalten Eintr\u00e4ge f\u00fcr Synonyme und Tochtertaxa von +list.resultsreturned.div01.des02=Platzhalter +list.resultsreturned.div01.des03=Platzhalter-Link +list.resultsreturned.form.des01=Die Ergebnisse enthalten Datens\u00e4tze, die unter den folgenden Namen \u00fcbermittelt wurden +list.resultsreturned.form.button01=Suche verfeinern +list.resultsreturned.form.div01=Platzhalter Taxaliste list.alert.title=E-Mail-Benachrichtigungen list.alert.navigator01=E-Mail-Benachrichtigungen f\u00fcr neue Dateneintr\u00e4ge erhalten list.alert.navigator02=E-Mail-Benachrichtigungen f\u00fcr neue Anmerkungen erhalten @@ -1002,4 +1002,4 @@ dataResource=Datenressource dataProvider=DatenpartnerIn dataResourceName=Datenressource dataProviderName=DatenpartnerIn -list.taxa.notfound=Hinweis\: Kein passender Taxonname gefunden f\u00fcr {0} \ No newline at end of file +list.taxa.notfound=Hinweis\: Kein passender Taxonname gefunden f\u00fcr {0} diff --git a/grails-app/i18n/messages_de_AT.properties b/grails-app/i18n/messages_de_AT.properties index 1a61451a3..c0b8c1946 100644 --- a/grails-app/i18n/messages_de_AT.properties +++ b/grails-app/i18n/messages_de_AT.properties @@ -226,15 +226,15 @@ list.facetcheckboxes.navigator02=Keine list.facetcheckboxes.button.updatefacetoptions=Aktualisieren list.facetcheckboxes.button.resetfacetoptions=Auf Grundeinstellungen zur\u00fccksetzen list.facetcheckboxes.div02.title=Benutzerdefinierte Attribute -list.resultsretuened.span.returnedtext=Ergebnisse f\u00fcr -list.resultsretuened.button01=Alles l\u00f6schen -list.resultsretuened.navigator01=Platzhalter -list.resultsretuened.div01.des01=Die Suchergebnisse beinhalten Eintr\u00e4ge f\u00fcr Synonyme und Tochtertaxa von -list.resultsretuened.div01.des02=Platzhalter -list.resultsretuened.div01.des03=Platzhalter-Link -list.resultsretuened.form.des01=Die Ergebnisse enthalten Datens\u00e4tze, die unter den folgenden Namen \u00fcbermittelt wurden -list.resultsretuened.form.button01=Suche verfeinern -list.resultsretuened.form.div01=Platzhalter Taxaliste +list.resultsreturned.span.returnedtext=Ergebnisse f\u00fcr +list.resultsreturned.button01=Alles l\u00f6schen +list.resultsreturned.navigator01=Platzhalter +list.resultsreturned.div01.des01=Die Suchergebnisse beinhalten Eintr\u00e4ge f\u00fcr Synonyme und Tochtertaxa von +list.resultsreturned.div01.des02=Platzhalter +list.resultsreturned.div01.des03=Platzhalter-Link +list.resultsreturned.form.des01=Die Ergebnisse enthalten Datens\u00e4tze, die unter den folgenden Namen \u00fcbermittelt wurden +list.resultsreturned.form.button01=Suche verfeinern +list.resultsreturned.form.div01=Platzhalter Taxaliste list.alert.title=E-Mail-Benachrichtigungen list.alert.navigator01=E-Mail-Benachrichtigungen f\u00fcr neue Dateneintr\u00e4ge erhalten list.alert.navigator02=E-Mail-Benachrichtigungen f\u00fcr neue Anmerkungen erhalten @@ -1002,4 +1002,4 @@ dataResource=Datenressource dataProvider=DatenpartnerIn dataResourceName=Datenressource dataProviderName=DatenpartnerIn -list.taxa.notfound=Hinweis\: Kein passender Taxonname gefunden f\u00fcr {0} \ No newline at end of file +list.taxa.notfound=Hinweis\: Kein passender Taxonname gefunden f\u00fcr {0} diff --git a/grails-app/i18n/messages_de_LU.properties b/grails-app/i18n/messages_de_LU.properties index d906f8cfa..82cfb5170 100644 --- a/grails-app/i18n/messages_de_LU.properties +++ b/grails-app/i18n/messages_de_LU.properties @@ -226,15 +226,15 @@ list.facetcheckboxes.navigator02=Keine list.facetcheckboxes.button.updatefacetoptions=Aktualisieren list.facetcheckboxes.button.resetfacetoptions=Auf Grundeinstellungen zur\u00fccksetzen list.facetcheckboxes.div02.title=Benutzerdefinierte Attribute -list.resultsretuened.span.returnedtext=Ergebnisse f\u00fcr -list.resultsretuened.button01=Alles l\u00f6schen -list.resultsretuened.navigator01=Platzhalter -list.resultsretuened.div01.des01=Die Suchergebnisse beinhalten Eintr\u00e4ge f\u00fcr Synonyme und Tochtertaxa von -list.resultsretuened.div01.des02=Platzhalter -list.resultsretuened.div01.des03=Platzhalter-Link -list.resultsretuened.form.des01=Die Ergebnisse enthalten Datens\u00e4tze, die unter den folgenden Namen \u00fcbermittelt wurden -list.resultsretuened.form.button01=Suche verfeinern -list.resultsretuened.form.div01=Platzhalter Taxaliste +list.resultsreturned.span.returnedtext=Ergebnisse f\u00fcr +list.resultsreturned.button01=Alles l\u00f6schen +list.resultsreturned.navigator01=Platzhalter +list.resultsreturned.div01.des01=Die Suchergebnisse beinhalten Eintr\u00e4ge f\u00fcr Synonyme und Tochtertaxa von +list.resultsreturned.div01.des02=Platzhalter +list.resultsreturned.div01.des03=Platzhalter-Link +list.resultsreturned.form.des01=Die Ergebnisse enthalten Datens\u00e4tze, die unter den folgenden Namen \u00fcbermittelt wurden +list.resultsreturned.form.button01=Suche verfeinern +list.resultsreturned.form.div01=Platzhalter Taxaliste list.alert.title=E-Mail-Benachrichtigungen list.alert.navigator01=E-Mail-Benachrichtigungen f\u00fcr neue Dateneintr\u00e4ge erhalten list.alert.navigator02=E-Mail-Benachrichtigungen f\u00fcr neue Anmerkungen erhalten @@ -1002,4 +1002,4 @@ dataResource=Datenressource dataProvider=DatenpartnerIn dataResourceName=Datenressource dataProviderName=DatenpartnerIn -list.taxa.notfound=Hinweis\: Kein passender Taxonname gefunden f\u00fcr {0} \ No newline at end of file +list.taxa.notfound=Hinweis\: Kein passender Taxonname gefunden f\u00fcr {0} diff --git a/grails-app/i18n/messages_en.properties b/grails-app/i18n/messages_en.properties index 30e87063d..8aeb10013 100644 --- a/grails-app/i18n/messages_en.properties +++ b/grails-app/i18n/messages_en.properties @@ -234,23 +234,24 @@ list.facetcheckboxes.navigator02 = None list.facetcheckboxes.button.updatefacetoptions = Update list.facetcheckboxes.button.resetfacetoptions = Reset to defaults list.facetcheckboxes.div02.title = Custom facets -list.resultsretuened.span.returnedtotal = records returned of -list.resultsretuened.span.returnedtext = for -list.resultsretuened.click.to.remove.filters = Click to remove this filter -list.resultsretuened.spatial.filter = Spatial filter -list.resultsretuened.circle = CIRCLE -list.resultsretuened.button01 = Clear all -list.resultsretuened.button01.title = Click to clear all filters -list.resultsretuened.navigator01 = placeholder -list.resultsretuened.navigator01.title = view species page -list.resultsretuened.click.more.info = click for more info on this query -list.resultsretuened.div01.des01 = The search results include records for synonyms and child taxa of -list.resultsretuened.div01.des02 = placeholder -list.resultsretuened.div01.des03 = link placeholder -list.resultsretuened.form.des01 = The result set contains records provided under the following names -list.resultsretuened.form.button01 = Refine search -list.resultsretuened.restrict.results = Restrict results to the selected names -list.resultsretuened.form.div01 = placeholder taxa list +list.resultsreturned.span.returnedtotal = records returned of +list.resultsreturned.span.returnedtext = for +list.resultsreturned.span.returnedtext1 = results for +list.resultsreturned.click.to.remove.filters = Click to remove this filter +list.resultsreturned.spatial.filter = Spatial filter +list.resultsreturned.circle = CIRCLE +list.resultsreturned.button01 = Clear all +list.resultsreturned.button01.title = Click to clear all filters +list.resultsreturned.navigator01 = placeholder +list.resultsreturned.navigator01.title = view species page +list.resultsreturned.click.more.info = click for more info on this query +list.resultsreturned.div01.des01 = The search results include records for synonyms and child taxa of +list.resultsreturned.div01.des02 = placeholder +list.resultsreturned.div01.des03 = link placeholder +list.resultsreturned.form.des01 = The result set contains records provided under the following names +list.resultsreturned.form.button01 = Refine search +list.resultsreturned.restrict.results = Restrict results to the selected names +list.resultsreturned.form.div01 = placeholder taxa list list.alert.title = Email alerts list.alert.navigator01 = Get email alerts for new records list.alert.navigator01.title = Notify me when new records come online for this search @@ -334,7 +335,7 @@ eya.dialogconfirm02 = km radius of eya.dialogconfirm03 = Format: tab-delimited text file (called data.xls) eya.table.01.th01 = Group eya.table.01.th02 = Species -eya.table.02.th01 = Species +eya.table.02.th01 = Scientific Name eya.table.02.th01.a = Common Name eya.table.02.th02 = Records eya.table.03.td = Records @@ -1112,7 +1113,17 @@ qualityFilter.filter.unique.error=The filter value must be unique quality.filters.group.title = Data Profile quality.filters.resetsearch.tooltip = Reset filters dq.profileinfo.button.tooltip = Click to view the profile description +dq.profilesettings.warning.appliedtosearch = Your default profile is applied to searches unless you select another profile from the data profiles drop down +dq.profilesettings.button.label = Settings +dq.profilesettings.button.tooltip = Data profile settings +dq.profilesettings.button.cancel = Cancel +dq.profilesettings.button.save = Save +dq.profilesettings.label.defaultprofile = Default profile +dq.profilesettings.label.showexpend = Show data profile details +dq.profilesettings.select.option.collapsed = Collapsed +dq.profilesettings.select.option.expanded = Expanded dq.categoryinfo.button.tooltip = Click for more information and actions +dq.prefsettings.dlg.title = Data profile user settings dq.categoryinfo.dlg.closebutton.text = Close dq.categoryinfo.dlg.expandbutton.text = Expand and edit filters dq.categoryinfo.dlg.fieldtable.heading.name = Field name @@ -1150,8 +1161,10 @@ dq.selectmultiple.form.cancel = Cancel dq.selectmultiple.form.submit = Apply dq.view.excluded = View excluded records dq.excluded.count = records are excluded by this category -dq.warning.dataprofile.title = Results filtering with Data profiles -dq.warning.dataprofile.content.line1 = Search results are now filtered by default to exclude lower quality records according to the ALA General data profile. Profiles may be disabled or other profiles are available via the Data Profile drop down. +dq.warning.dataprofile.title = Results filtering with data profiles +dq.warning.dataprofile.content.line1 = Search results are now filtered by default to exclude lower quality records according to the ALA General data profile. Data profiles may be disabled or other data profiles are available via the data profile drop down. dq.warning.dataprofile.buttonleft.text = Learn More dq.warning.dataprofile.buttonright.text = Got it - +dq.userpref.defaultprofile = -- Select a profile -- +dq.data.profiles.disabled = Data profiles have been disabled for this search +dq.warning.failedtosave = Failed to save user preferences. Please try again diff --git a/grails-app/i18n/messages_es.properties b/grails-app/i18n/messages_es.properties index 30ce4d715..697e4f082 100644 --- a/grails-app/i18n/messages_es.properties +++ b/grails-app/i18n/messages_es.properties @@ -227,15 +227,15 @@ list.facetcheckboxes.navigator02=Ninguno list.facetcheckboxes.button.updatefacetoptions=Actualizar list.facetcheckboxes.button.resetfacetoptions=Restablecer los valores predeterminados list.facetcheckboxes.div02.title=Facetas personalizadas -list.resultsretuened.span.returnedtext=resultados para -list.resultsretuened.button01=Borrar todo -list.resultsretuened.navigator01=marcador de posici\u00f3n -list.resultsretuened.div01.des01=Los resultados de las b\u00fasquedas incluten registros de sin\u00f3nimos e hijos del taxon de -list.resultsretuened.div01.des02=marcador de posici\u00f3n -list.resultsretuened.div01.des03=Enlace marcador de posici\u00f3n -list.resultsretuened.form.des01=El conjunto de resultados contiene registros proporcionados bajo los siguientes nombres -list.resultsretuened.form.button01=Filtrar la b\u00fasqueda -list.resultsretuened.form.div01=lista de marcadores de posici\u00f3n del taxon +list.resultsreturned.span.returnedtext=resultados para +list.resultsreturned.button01=Borrar todo +list.resultsreturned.navigator01=marcador de posici\u00f3n +list.resultsreturned.div01.des01=Los resultados de las b\u00fasquedas incluten registros de sin\u00f3nimos e hijos del taxon de +list.resultsreturned.div01.des02=marcador de posici\u00f3n +list.resultsreturned.div01.des03=Enlace marcador de posici\u00f3n +list.resultsreturned.form.des01=El conjunto de resultados contiene registros proporcionados bajo los siguientes nombres +list.resultsreturned.form.button01=Filtrar la b\u00fasqueda +list.resultsreturned.form.div01=lista de marcadores de posici\u00f3n del taxon list.alert.title=Alertas de correo electr\u00f3nico list.alert.navigator01=Recibir alertas de correo electr\u00f3nico para los nuevos registros list.alert.navigator02=Recibir alertas de correo electr\u00f3nico para nuevas anotaciones @@ -1003,4 +1003,4 @@ dataResource=Fuente de los datos dataProvider=Socio de datos dataResourceName=Fuente de los datos dataProviderName=Socio de datos -list.taxa.notfound=Nota\: no se ha encontrado coincidencia en el nombre del taxon para {0} \ No newline at end of file +list.taxa.notfound=Nota\: no se ha encontrado coincidencia en el nombre del taxon para {0} diff --git a/grails-app/i18n/messages_eu.properties b/grails-app/i18n/messages_eu.properties index 320a97b24..4851175fa 100644 --- a/grails-app/i18n/messages_eu.properties +++ b/grails-app/i18n/messages_eu.properties @@ -192,15 +192,15 @@ list.facetcheckboxes.navigator02=Bat ere ez list.facetcheckboxes.button.updatefacetoptions=Eguneratu list.facetcheckboxes.button.resetfacetoptions=Berrezarri balio lehenetsiak list.facetcheckboxes.div02.title=Fazeta pertsonalizatuak -list.resultsretuened.span.returnedtext=Emaitzak, ondorengo honen arabera -list.resultsretuened.button01=Ezabatu dena -list.resultsretuened.navigator01=Leku-marka -list.resultsretuened.div01.des01=Bilaketaren emaitzek ondorengo honen sinonimo errejistroak eta taxon-umeak dituzte -list.resultsretuened.div01.des02=Leku-marka -list.resultsretuened.div01.des03=Leku-markaren esteka -list.resultsretuened.form.des01=Emaitza multzoak ondorengo izenekin emandako erregistroak ditu -list.resultsretuened.form.button01=Zehaztu bilaketa -list.resultsretuened.form.div01=Taxonaren leku-marken zerrenda +list.resultsreturned.span.returnedtext=Emaitzak, ondorengo honen arabera +list.resultsreturned.button01=Ezabatu dena +list.resultsreturned.navigator01=Leku-marka +list.resultsreturned.div01.des01=Bilaketaren emaitzek ondorengo honen sinonimo errejistroak eta taxon-umeak dituzte +list.resultsreturned.div01.des02=Leku-marka +list.resultsreturned.div01.des03=Leku-markaren esteka +list.resultsreturned.form.des01=Emaitza multzoak ondorengo izenekin emandako erregistroak ditu +list.resultsreturned.form.button01=Zehaztu bilaketa +list.resultsreturned.form.div01=Taxonaren leku-marken zerrenda list.alert.title=Alertarako e-postak list.alert.navigator01=Jaso alertarako e-postak erregistro berriak daudenean list.alert.navigator02=Jaso alertarako e-postak ohar berriak daudenean @@ -469,4 +469,4 @@ month.12=Abendua # Darwin core terms - record terms -# Additional terms \ No newline at end of file +# Additional terms diff --git a/grails-app/i18n/messages_fr.properties b/grails-app/i18n/messages_fr.properties index 0fbaccf56..0170f3f91 100644 --- a/grails-app/i18n/messages_fr.properties +++ b/grails-app/i18n/messages_fr.properties @@ -227,15 +227,15 @@ list.facetcheckboxes.navigator02=Aucun list.facetcheckboxes.button.updatefacetoptions=Mettre \u00e0 jour list.facetcheckboxes.button.resetfacetoptions=R\u00e9tablir les valeurs par d\u00e9faut list.facetcheckboxes.div02.title=Facettes personnalis\u00e9s -list.resultsretuened.span.returnedtext=r\u00e9sultats pour -list.resultsretuened.button01=Tout effacer -list.resultsretuened.navigator01=marqueur de position -list.resultsretuened.div01.des01=Les r\u00e9sultats de cette recherche incluent des enregistrements relatif aux synonymes et aux enfants du taxon -list.resultsretuened.div01.des02=emplacement r\u00e9serv\u00e9 -list.resultsretuened.div01.des03=lien du marqueur de position -list.resultsretuened.form.des01=Les r\u00e9sultats contiennent des enregistrements affich\u00e9s sous les noms suivants -list.resultsretuened.form.button01=Affiner la recherche -list.resultsretuened.form.div01=liste des taxons d'espace r\u00e9serv\u00e9 +list.resultsreturned.span.returnedtext=r\u00e9sultats pour +list.resultsreturned.button01=Tout effacer +list.resultsreturned.navigator01=marqueur de position +list.resultsreturned.div01.des01=Les r\u00e9sultats de cette recherche incluent des enregistrements relatif aux synonymes et aux enfants du taxon +list.resultsreturned.div01.des02=emplacement r\u00e9serv\u00e9 +list.resultsreturned.div01.des03=lien du marqueur de position +list.resultsreturned.form.des01=Les r\u00e9sultats contiennent des enregistrements affich\u00e9s sous les noms suivants +list.resultsreturned.form.button01=Affiner la recherche +list.resultsreturned.form.div01=liste des taxons d'espace r\u00e9serv\u00e9 list.alert.title=Alertes email list.alert.navigator01=Recevoir des alertes par email pour les nouveaux enregistrements list.alert.navigator02=Recevoir des alertes par email pour les nouvelles annotations @@ -936,4 +936,4 @@ subspecies=Sous-esp\u00e8ce dataResource=Jeux de donn\u00e9es dataResourceName=Jeux de donn\u00e9es dataProviderName=Fournisseurs de donn\u00e9es -list.taxa.notfound=Note \: aucun nom de taxon trouv\u00e9 pour {0} \ No newline at end of file +list.taxa.notfound=Note \: aucun nom de taxon trouv\u00e9 pour {0} diff --git a/grails-app/i18n/messages_pt_BR.properties b/grails-app/i18n/messages_pt_BR.properties index 264903038..37d03c9aa 100644 --- a/grails-app/i18n/messages_pt_BR.properties +++ b/grails-app/i18n/messages_pt_BR.properties @@ -225,15 +225,15 @@ list.facetcheckboxes.navigator02=Nenhum list.facetcheckboxes.button.updatefacetoptions=Atualizar list.facetcheckboxes.button.resetfacetoptions=Redefinir para padr\u00e3o list.facetcheckboxes.div02.title=Facetas personalizadas -list.resultsretuened.span.returnedtext=resultados para -list.resultsretuened.button01=Limpar tudo -list.resultsretuened.navigator01=marcador de posi\u00e7\u00e3o -list.resultsretuened.div01.des01=Os resultados da pesquisa incluem registros para sin\u00f4nimos e taxa filhos de -list.resultsretuened.div01.des02=marcador de posi\u00e7\u00e3o -list.resultsretuened.div01.des03=link do marcador de posi\u00e7\u00e3o -list.resultsretuened.form.des01=O conjunto de resultados cont\u00e9m registros fornecidos com os seguintes nomes -list.resultsretuened.form.button01=Refinar busca -list.resultsretuened.form.div01=lista de t\u00e1xons de espa\u00e7o reservado +list.resultsreturned.span.returnedtext=resultados para +list.resultsreturned.button01=Limpar tudo +list.resultsreturned.navigator01=marcador de posi\u00e7\u00e3o +list.resultsreturned.div01.des01=Os resultados da pesquisa incluem registros para sin\u00f4nimos e taxa filhos de +list.resultsreturned.div01.des02=marcador de posi\u00e7\u00e3o +list.resultsreturned.div01.des03=link do marcador de posi\u00e7\u00e3o +list.resultsreturned.form.des01=O conjunto de resultados cont\u00e9m registros fornecidos com os seguintes nomes +list.resultsreturned.form.button01=Refinar busca +list.resultsreturned.form.div01=lista de t\u00e1xons de espa\u00e7o reservado list.alert.title=Alertas por email list.alert.navigator01=Receba alertas por email para novos registros list.alert.navigator02=Receba alertas por email para novos registros @@ -999,4 +999,4 @@ subspecies=Subesp\u00e9cies dataResource=Recurso de dados dataProvider=Parceiro de dados dataResourceName=Recurso de dados -dataProviderName=Parceiro de dados \ No newline at end of file +dataProviderName=Parceiro de dados diff --git a/grails-app/i18n/messages_pt_PT.properties b/grails-app/i18n/messages_pt_PT.properties index f1f98a6b1..8834f3b50 100644 --- a/grails-app/i18n/messages_pt_PT.properties +++ b/grails-app/i18n/messages_pt_PT.properties @@ -204,15 +204,15 @@ list.facetcheckboxes.navigator02=Nenhum list.facetcheckboxes.button.updatefacetoptions=Atualizar list.facetcheckboxes.button.resetfacetoptions=Repor defini\u00e7\u00f5es por defeito list.facetcheckboxes.div02.title=Facetas personalizadas -list.resultsretuened.span.returnedtext=resultados para -list.resultsretuened.button01=Limpar tudo -list.resultsretuened.navigator01=marcador de posi\u00e7\u00e3o -list.resultsretuened.div01.des01=Os resultados da pesquisa incluem registos para sin\u00f3nimos e taxons filhos de -list.resultsretuened.div01.des02=marcador de posi\u00e7\u00e3o -list.resultsretuened.div01.des03=link marcador de posi\u00e7\u00e3o -list.resultsretuened.form.des01=O conjunto de resultados cont\u00e9m registos fornecidos com os seguintes nomes -list.resultsretuened.form.button01=Refinar pesquisa -list.resultsretuened.form.div01=lista de marcadores de posi\u00e7\u00e3o dos taxa +list.resultsreturned.span.returnedtext=resultados para +list.resultsreturned.button01=Limpar tudo +list.resultsreturned.navigator01=marcador de posi\u00e7\u00e3o +list.resultsreturned.div01.des01=Os resultados da pesquisa incluem registos para sin\u00f3nimos e taxons filhos de +list.resultsreturned.div01.des02=marcador de posi\u00e7\u00e3o +list.resultsreturned.div01.des03=link marcador de posi\u00e7\u00e3o +list.resultsreturned.form.des01=O conjunto de resultados cont\u00e9m registos fornecidos com os seguintes nomes +list.resultsreturned.form.button01=Refinar pesquisa +list.resultsreturned.form.div01=lista de marcadores de posi\u00e7\u00e3o dos taxa list.alert.title=Alertas por email list.alert.navigator01=Receba alertas de email para novos registos list.alert.navigator02=Receba alertas de email para novas anota\u00e7\u00f5es @@ -483,4 +483,4 @@ month.12=Dezembro # Darwin core terms - record terms -# Additional terms \ No newline at end of file +# Additional terms diff --git a/grails-app/i18n/messages_zh.properties b/grails-app/i18n/messages_zh.properties index 28e164c78..f7f5045c9 100644 --- a/grails-app/i18n/messages_zh.properties +++ b/grails-app/i18n/messages_zh.properties @@ -205,15 +205,15 @@ list.facetcheckboxes.navigator02=\u6ca1\u6709 list.facetcheckboxes.button.updatefacetoptions=\u66f4\u65b0 list.facetcheckboxes.button.resetfacetoptions=\u8bbe\u7f6e\u4e3a\u7f3a\u7701 list.facetcheckboxes.div02.title=\u5b9a\u5236\u5c0f\u5e73\u9762 -list.resultsretuened.span.returnedtext=\u7ed3\u679c\u6765\u81ea -list.resultsretuened.button01=\u6e05\u9664\u6240\u6709 -list.resultsretuened.navigator01=\u5360\u4f4d\u7b26 -list.resultsretuened.div01.des01=\u67e5\u8be2\u7ed3\u679c\u5305\u542b\u8fd1\u4e49\u7684\u548c\u5b50\u5206\u7c7b\u5355\u5143\u7684\u8bb0\u5f55 -list.resultsretuened.div01.des02=\u5360\u4f4d\u7b26 -list.resultsretuened.div01.des03=\u94fe\u63a5\u5360\u4f4d\u7b26 -list.resultsretuened.form.des01=\u7ed3\u679c\u96c6\u5408\u4e2d\u542b\u6709\u5728\u4e0b\u9762\u540d\u4e0b\u7684\u8bb0\u5f55 -list.resultsretuened.form.button01=\u4f18\u5316\u67e5\u8be2 -list.resultsretuened.form.div01=\u5360\u4f4d\u7b26\u5206\u7c7b\u5355\u5143\u5217\u8868 +list.resultsreturned.span.returnedtext=\u7ed3\u679c\u6765\u81ea +list.resultsreturned.button01=\u6e05\u9664\u6240\u6709 +list.resultsreturned.navigator01=\u5360\u4f4d\u7b26 +list.resultsreturned.div01.des01=\u67e5\u8be2\u7ed3\u679c\u5305\u542b\u8fd1\u4e49\u7684\u548c\u5b50\u5206\u7c7b\u5355\u5143\u7684\u8bb0\u5f55 +list.resultsreturned.div01.des02=\u5360\u4f4d\u7b26 +list.resultsreturned.div01.des03=\u94fe\u63a5\u5360\u4f4d\u7b26 +list.resultsreturned.form.des01=\u7ed3\u679c\u96c6\u5408\u4e2d\u542b\u6709\u5728\u4e0b\u9762\u540d\u4e0b\u7684\u8bb0\u5f55 +list.resultsreturned.form.button01=\u4f18\u5316\u67e5\u8be2 +list.resultsreturned.form.div01=\u5360\u4f4d\u7b26\u5206\u7c7b\u5355\u5143\u5217\u8868 list.alert.title=\u90ae\u4ef6\u8b66\u544a list.alert.navigator01=\u4e3a\u65b0\u8bb0\u5f55\u53d6\u90ae\u4ef6\u8b66\u544a list.alert.navigator02=\u4e3a\u65b0\u6807\u7b7e\u53d6\u90ae\u4ef6\u8b66\u544a @@ -463,4 +463,4 @@ month.12=\u5341\u4e8c\u6708 # Darwin core terms - record terms -# Additional terms \ No newline at end of file +# Additional terms diff --git a/grails-app/services/au/org/ala/biocache/hubs/PostProcessingService.groovy b/grails-app/services/au/org/ala/biocache/hubs/PostProcessingService.groovy index 29a0b1c43..c407bee04 100644 --- a/grails-app/services/au/org/ala/biocache/hubs/PostProcessingService.groovy +++ b/grails-app/services/au/org/ala/biocache/hubs/PostProcessingService.groovy @@ -229,7 +229,7 @@ class PostProcessingService { * @param defaultValue * @return */ - private static String getCookieValue(Cookie[] cookies, String cookieName, String defaultValue) { + static String getCookieValue(Cookie[] cookies, String cookieName, String defaultValue) { String cookieValue = defaultValue // fall back cookies.each { cookie -> @@ -504,7 +504,12 @@ class PostProcessingService { layerObjects } - def processUserFQInteraction(SpatialSearchRequestParams requestParams, activeFacetObj) { + Map processUserFQInteraction(SpatialSearchRequestParams requestParams, activeFacetObj) { + def interactionMap = [:] + if (requestParams.disableAllQualityFilters) { + return interactionMap + } + def disabled = requestParams.disableQualityFilter as Set // map from category label to filter names @@ -513,13 +518,11 @@ class PostProcessingService { // This means a user filter can interact with a DQ filter even when its exclude count == 0 def categoryToKeyMap = [:] - if (!requestParams.disableAllQualityFilters) { - categoryToKeyMap = qualityService.getGroupedEnabledFilters(requestParams.qualityProfile).findAll { label, list -> - !disabled.contains(label) - }.collectEntries { label, list -> - def keys = list*.filter.collect { getKeysFromFilter(it) }.flatten() - keys.isEmpty() ? [:] : [(label): keys as Set] - } + categoryToKeyMap = qualityService.getGroupedEnabledFilters(requestParams.qualityProfile).findAll { label, list -> + !disabled.contains(label) + }.collectEntries { label, list -> + def keys = list*.filter.collect { getKeysFromFilter(it) }.flatten() + keys.isEmpty() ? [:] : [(label): keys as Set] } def profile = qualityService.activeProfile(requestParams.qualityProfile) @@ -587,7 +590,11 @@ class PostProcessingService { it.value.each { DQColors.put(it, color) } } - return [fqInteract, dqInteract, UserFQColors, DQColors] + interactionMap.fqInteract = fqInteract + interactionMap.dqInteract = dqInteract + interactionMap.UserFQColors = UserFQColors + interactionMap.DQColors = DQColors + interactionMap } /** diff --git a/grails-app/services/au/org/ala/biocache/hubs/QualityService.groovy b/grails-app/services/au/org/ala/biocache/hubs/QualityService.groovy index a258ae637..77eeb2604 100644 --- a/grails-app/services/au/org/ala/biocache/hubs/QualityService.groovy +++ b/grails-app/services/au/org/ala/biocache/hubs/QualityService.groovy @@ -141,6 +141,21 @@ class QualityService { } } + // QualityProfile.isDefault is not in the returned json (it's a bug should be fixed in dq-service side) + // this is just a workaround: go through each enabled profile to match the name + boolean isProfileEnabled(String profileShortName) { + if (dataQualityEnabled) { + def enabledProfiles = findAllEnabledProfiles(true) + return enabledProfiles?.any { it.shortName == profileShortName} + } else { + return false + } + } + + boolean isProfileValid(profileName) { + return profileName && isProfileEnabled(profileName) + } + def clearRecordCountCache() { recordCountCache.invalidateAll() } 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 edf08d2d1..089e9b47b 100644 --- a/grails-app/services/au/org/ala/biocache/hubs/UserDataService.groovy +++ b/grails-app/services/au/org/ala/biocache/hubs/UserDataService.groovy @@ -44,10 +44,14 @@ class UserDataService { data } - def set(userId, type, data) { + // return value indicates if set succeeds + boolean set(userId, type, data) { if (userId && grailsApplication.config.userdetails.baseUrl) { - webService.post(grailsApplication.config.userdetails.baseUrl + '/property/saveProperty', null, + def response = webService.post(grailsApplication.config.userdetails.baseUrl + '/property/saveProperty', null, [alaId: userId, name: NAME_PREFIX + type, value: (data as JSON).toString()]) + + return response?.statusCode == 200 } + return false } } diff --git a/grails-app/services/au/org/ala/biocache/hubs/UserService.groovy b/grails-app/services/au/org/ala/biocache/hubs/UserService.groovy index e15edb91d..0ee8cc827 100644 --- a/grails-app/services/au/org/ala/biocache/hubs/UserService.groovy +++ b/grails-app/services/au/org/ala/biocache/hubs/UserService.groovy @@ -14,9 +14,17 @@ package au.org.ala.biocache.hubs import au.org.ala.web.UserDetails +import groovy.json.JsonSlurper +import org.springframework.beans.factory.annotation.Value + +import javax.servlet.http.HttpServletRequest class UserService { - def authService + def authService, userDataService, grailsApplication + + @Value('${dataquality.enabled}') + boolean dataQualityEnabled + /** * Get both email and displayName for a numeric user id. Preferring to use the auth service * unless it's unavailable, then fall back to database @@ -40,4 +48,31 @@ class UserService { log.warn('could not find user details') return [displayName: userid, email: userid] } - }} + } + + Map getUserPref(String userId, HttpServletRequest request) { + def pref = [:] + if (dataQualityEnabled) { + def prefKey = "${grailsApplication.config.dataquality.prefkey}" + if (userId != null) { // retrieve data from userdetails + pref = userDataService.get(userId, prefKey) + } else { // use cookie + def rawCookie = PostProcessingService.getCookieValue(request.getCookies(), prefKey, null) + + if (rawCookie) { + try { + pref = new JsonSlurper().parseText(URLDecoder.decode(rawCookie, "UTF-8")) + } catch (UnsupportedEncodingException ex) { + log.error(ex.getMessage(), ex) + } + } + } + + // make sure values exists + pref.disableAll = pref?.containsKey('disableAll') ? pref.disableAll : false + pref.dataProfile = pref?.containsKey('dataProfile') ? pref.dataProfile : null + pref.expand = pref?.containsKey('expand') ? pref.expand : true + } + pref + } +} diff --git a/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy b/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy index 5321e86ca..4101522c0 100644 --- a/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy +++ b/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy @@ -647,9 +647,8 @@ class WebServicesService { def populateProfile(requestParams) { // force set the profile if none provided - if (dataQualityEnabled && !requestParams.qualityProfile && !requestParams.disableAllQualityFilters) { - def activeProfile = qualityService.activeProfile(requestParams.qualityProfile) - requestParams.qualityProfile = activeProfile?.shortName + if (dataQualityEnabled && !qualityService.isProfileValid(requestParams.qualityProfile) && !requestParams.disableAllQualityFilters) { + requestParams.qualityProfile = qualityService.activeProfile()?.shortName } } } 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 c148326fc..3ae547ce1 100644 --- a/grails-app/taglib/au/org/ala/biocache/hubs/OccurrenceTagLib.groovy +++ b/grails-app/taglib/au/org/ala/biocache/hubs/OccurrenceTagLib.groovy @@ -1203,4 +1203,22 @@ class OccurrenceTagLib { out << g.link(attrs, body) } + + def resultCount = { attrs, body -> + def mb = new MarkupBuilder(out) + + if (dataQualityEnabled) { + mb.span(id:'returnedText') { + strong g.formatNumber(number: "${attrs.totalRecords}", format: "#,###,###") + span alatag.message(code: "list.resultsreturned.span.returnedtotal", default: 'records returned of') + strong g.formatNumber(number: "${attrs.qualityTotalCount}", format:"#,###,###") + span alatag.message(code:"list.resultsreturned.span.returnedtext", default:'for') + } + } else { + mb.span(id:'returnedText') { + strong g.formatNumber(number: "${attrs.totalRecords}", format: "#,###,###") + span alatag.message(code:"list.resultsreturned.span.returnedtext1", default:'results for') + } + } + } } diff --git a/grails-app/views/occurrence/_recordSidebar.gsp b/grails-app/views/occurrence/_recordSidebar.gsp index 6a3c078cf..7c4d4b143 100644 --- a/grails-app/views/occurrence/_recordSidebar.gsp +++ b/grails-app/views/occurrence/_recordSidebar.gsp @@ -240,13 +240,13 @@ : ${raw(record.raw.miscProperties.TITLE)}
- : ${record.raw.occurrence.photographer ?: image.metadata?.creator}
+ : ${image.metadata?.creator ?: record.raw.occurrence.photographer}
- : ${record.raw.occurrence.rights ?: image.metadata?.rights}
+ : ${image.metadata?.rights ?: record.raw.occurrence.rights}
- - : ${record.raw.occurrence.rightsholder ?: image.metadata?.rightsholder}
+ + : ${image.metadata?.rightsHolder ?: record.raw.occurrence.rightsholder}
: ${record.raw.miscProperties.rightsHolder}
diff --git a/grails-app/views/occurrence/exploreYourArea.gsp b/grails-app/views/occurrence/exploreYourArea.gsp index a24db85d8..ac926788c 100644 --- a/grails-app/views/occurrence/exploreYourArea.gsp +++ b/grails-app/views/occurrence/exploreYourArea.gsp @@ -161,9 +161,8 @@    -
- : - + + diff --git a/grails-app/views/occurrence/list.gsp b/grails-app/views/occurrence/list.gsp index 55558658a..1fc96eb06 100644 --- a/grails-app/views/occurrence/list.gsp +++ b/grails-app/views/occurrence/list.gsp @@ -69,7 +69,11 @@ ' Image does not support the identification of the species, subject is unclear and identifying features are difficult to see or not visible.
', savePreferredSpeciesListUrl: "${createLink(controller: 'imageClient', action: 'saveImageToSpeciesList')}", getPreferredSpeciesListUrl: "${createLink(controller: 'imageClient', action: 'getPreferredSpeciesImageList')}", - excludeCountUrl: "${createLink(controller: 'occurrence', action: 'dataQualityExcludeCounts', params: params.clone()).encodeAsJavaScript()}" + excludeCountUrl: "${createLink(controller: 'occurrence', action: 'dataQualityExcludeCounts', params: params.clone()).encodeAsJavaScript()}", + expandProfileDetails: ${grailsApplication.config.getProperty('dataquality.enabled', Boolean, false) ? expandProfileDetails : true}, + userId: "${userId}", + prefKey: "${(grailsApplication.config.getProperty("dataquality.prefkey", String, "dqUserProfile"))}", + expandKey: "${(grailsApplication.config.getProperty("dataquality.expandKey", String, "dqDetailExpand"))}" }; @@ -290,12 +294,7 @@
- - - - - - + ${raw(queryDisplay)} ${raw(queryToShow) ?: params.taxa ?: params.q} @@ -305,20 +304,20 @@ () - +