diff --git a/.travis.yml b/.travis.yml
index 65508237..09eb38fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,8 @@ branches:
only:
- master
- develop
- - feature/issue-175
+ - /^feature\/.*$/
+ - /^bugfix\/.*$/
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
diff --git a/build.gradle b/build.gradle
index accee440..f77ceebb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,20 +6,18 @@ buildscript {
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
- classpath "org.grails.plugins:hibernate5:7.0.5"
- classpath "gradle.plugin.com.github.erdi.webdriver-binaries:webdriver-binaries-gradle-plugin:2.0"
- classpath "com.bertramlabs.plugins:asset-pipeline-gradle:3.3.2"
- classpath 'org.grails.plugins:database-migration:3.1.0'
+ classpath "org.grails.plugins:hibernate5:${gormVersion}"
+ classpath "gradle.plugin.com.github.erdi.webdriver-binaries:webdriver-binaries-gradle-plugin:$webdriverBinariesVersion"
+ classpath "com.bertramlabs.plugins:asset-pipeline-gradle:3.4.6"
+ classpath 'org.grails.plugins:database-migration:4.2.0'
}
}
plugins {
- id "com.gorylenko.gradle-git-properties" version "2.4.0-rc2"
+ id "com.gorylenko.gradle-git-properties" version "2.4.1"
}
-
-version "3.1.3"
-
+version "4.0.0-SNAPSHOT"
group "au.org.ala"
apply plugin:"eclipse"
@@ -31,12 +29,9 @@ apply plugin:"com.bertramlabs.asset-pipeline"
apply plugin:"org.grails.grails-gsp"
apply plugin:"maven-publish"
-bootJar {
- enabled = true
- classifier = 'exec'
-}
publishing {
+ targetCompatibility = 1.11
repositories {
maven {
name 'Nexus'
@@ -49,34 +44,15 @@ publishing {
}
publications {
mavenJar(MavenPublication) {
- pom.withXml {
- def pomNode = asNode()
- pomNode.dependencyManagement.replaceNode {}
-
- // simply remove dependencies without a version
- // version-less dependencies are handled with dependencyManagement
- // see https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/8 for more complete solutions
- pomNode.dependencies.dependency.findAll {
- it.version.text().isEmpty()
- }.each {
- it.replaceNode {}
- }
- }
- from components.web
-// if (bootJar && bootJar.enabled && bootJar.classifier) {
-// def repackagedFile = file("$libsDir/$project.name-$project.version-${bootJar.classifier}.jar")
-// if (repackagedFile.exists()) {
-// artifact(repackagedFile) {
-// classifier bootRepackage.classifier
-// }
-// } else {
-// logger.quiet("Spring Boot repackage with classifier specified but file is not present!")
-// }
-// }
+ artifact bootWar
}
}
}
+bootWar {
+ launchScript()
+}
+
repositories {
mavenLocal()
maven { url "https://nexus.ala.org.au/content/groups/public/" }
@@ -100,92 +76,88 @@ sourceSets {
dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
- compile "org.springframework.boot:spring-boot-starter-logging"
- compile "org.springframework.boot:spring-boot-autoconfigure"
- compile "org.grails:grails-core"
- compile "org.springframework.boot:spring-boot-starter-actuator"
- compile "org.springframework.boot:spring-boot-starter-tomcat"
- compile "org.grails:grails-web-boot"
- compile "org.grails:grails-logging"
- compile "org.grails:grails-plugin-rest"
- compile "org.grails:grails-plugin-databinding"
- compile "org.grails:grails-plugin-i18n"
- compile "org.grails:grails-plugin-services"
- compile "org.grails:grails-plugin-url-mappings"
- compile "org.grails:grails-plugin-interceptors"
- compile "org.grails.plugins:cache"
- compile "org.grails.plugins:async"
- compile "org.grails.plugins:scaffolding"
- compile "org.grails.plugins:events"
- compile "org.grails.plugins:hibernate5"
- compile "org.hibernate:hibernate-core:5.4.18.Final"
- compile "org.grails.plugins:gsp"
-
- compile "com.opencsv:opencsv:3.7"
- compile "io.micronaut:micronaut-http-client"
+ implementation "org.springframework.boot:spring-boot-starter-logging"
+ implementation "org.springframework.boot:spring-boot-autoconfigure"
+ implementation "org.grails:grails-core"
+ implementation "org.springframework.boot:spring-boot-starter-actuator"
+ implementation "org.springframework.boot:spring-boot-starter-tomcat"
+ implementation "org.grails:grails-web-boot"
+ implementation "org.grails:grails-logging"
+ implementation "org.grails:grails-plugin-rest"
+ implementation "org.grails:grails-plugin-databinding"
+ implementation "org.grails:grails-plugin-i18n"
+ implementation "org.grails:grails-plugin-services"
+ implementation "org.grails:grails-plugin-url-mappings"
+ implementation "org.grails:grails-plugin-interceptors"
+ implementation "org.grails.plugins:cache"
+ implementation "org.grails.plugins:async"
+ implementation "org.grails.plugins:scaffolding"
+ implementation "org.grails.plugins:events"
+ implementation "org.grails.plugins:hibernate5"
+ implementation "org.hibernate:hibernate-core:5.4.18.Final"
+ implementation "org.grails.plugins:gsp"
+
+ implementation "com.opencsv:opencsv:3.7"
+ implementation "io.micronaut:micronaut-http-client"
compileOnly "io.micronaut:micronaut-inject-groovy"
- compile "org.apache.httpcomponents:httpclient:4.5.13"
- compile "commons-io:commons-io:2.11.0"
+ implementation "io.reactivex.rxjava3:rxjava:3.1.6"
+ implementation "org.apache.httpcomponents:httpclient:4.5.13"
+ implementation "commons-io:commons-io:2.11.0"
// See: https://github.com/AtlasOfLivingAustralia/collectory/issues/84#issuecomment-1070670979
// before updating mysql-connector-java
- compile "mysql:mysql-connector-java:8.0.22"
- compile "org.grails.plugins:ala-bootstrap3:4.1.0"
- compile "au.org.ala.plugins.grails:ala-charts-plugin:2.1.2"
- compile "org.grails.plugins:ala-auth:5.1.1"
- compile "org.grails.plugins:ala-ws-security-plugin:4.1.2"
- compile "org.grails.plugins:ala-ws-plugin:3.1.1"
- compile "org.grails.plugins:audit-logging:4.0.3"
- compile "org.grails.plugins:external-config:2.0.0"
- compile "org.grails.plugins:ala-admin-plugin:2.3.0"
- compile 'au.org.ala.plugins:openapi:1.1.0'
+ implementation "mysql:mysql-connector-java:8.0.22"
+ implementation "org.grails.plugins:ala-bootstrap3:4.1.0"
+ implementation "au.org.ala.plugins.grails:ala-charts-plugin:2.3.0"
+ implementation "org.grails.plugins:ala-auth:$alaSecurityLibsVersion"
+ implementation "org.grails.plugins:ala-ws-security-plugin:$alaSecurityLibsVersion"
+ implementation "org.grails.plugins:ala-ws-plugin:$alaSecurityLibsVersion"
+ implementation "org.grails.plugins:audit-logging:4.0.3"
+ implementation 'dk.glasius:external-config:3.1.1'
+ implementation "org.grails.plugins:ala-admin-plugin:2.3.0"
+ implementation 'au.org.ala.plugins:openapi:1.1.0'
console "org.grails:grails-console"
profile "org.grails.profiles:web"
- runtime "org.glassfish.web:el-impl:2.1.2-b03"
- runtime "com.h2database:h2"
- runtime "org.apache.tomcat:tomcat-jdbc"
- runtime "javax.xml.bind:jaxb-api:2.3.1"
- runtime "com.bertramlabs.plugins:asset-pipeline-grails:3.2.4"
- testCompile "io.micronaut:micronaut-inject-groovy"
- testCompile "org.grails:grails-gorm-testing-support"
- testCompile "org.mockito:mockito-core"
- testCompile "org.grails:grails-web-testing-support"
- testCompile "org.grails.plugins:geb"
- testCompile "org.seleniumhq.selenium:selenium-remote-driver:3.141.59"
- testCompile "org.seleniumhq.selenium:selenium-api:3.141.59"
- testCompile "org.seleniumhq.selenium:selenium-support:3.141.59"
- testRuntime "org.seleniumhq.selenium:selenium-chrome-driver:3.141.59"
- testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:3.141.59"
- compile 'org.grails.plugins:sentry:11.7.25'
- // In newer versions use 'implementation'
- compile 'org.liquibase:liquibase-core:3.10.3'
- compile "org.grails.plugins:database-migration:3.1.0"
+ runtimeOnly "org.glassfish.web:el-impl:2.1.2-b03"
+ runtimeOnly "com.h2database:h2"
+ runtimeOnly "org.apache.tomcat:tomcat-jdbc"
+ runtimeOnly "javax.xml.bind:jaxb-api:2.3.1"
+ runtimeOnly "com.bertramlabs.plugins:asset-pipeline-grails:3.4.6"
+ testImplementation "io.micronaut:micronaut-inject-groovy"
+ testImplementation "org.grails:grails-gorm-testing-support"
+ testImplementation "org.mockito:mockito-core"
+ testImplementation "org.grails:grails-web-testing-support"
+ testImplementation "org.grails.plugins:geb"
+ testImplementation "org.seleniumhq.selenium:selenium-remote-driver:$seleniumVersion"
+ testImplementation "org.seleniumhq.selenium:selenium-api:$seleniumVersion"
+ testImplementation "org.seleniumhq.selenium:selenium-support:$seleniumVersion"
+ runtimeOnly "net.sourceforge.htmlunit:htmlunit:2.18"
+ testImplementation "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
+ testImplementation "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
+ testImplementation "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion"
+ testImplementation "org.seleniumhq.selenium:selenium-safari-driver:$seleniumSafariDriverVersion"
+ implementation 'org.grails.plugins:sentry:11.7.25'
+ // db-migration
+ implementation 'org.liquibase:liquibase-core:4.19.0'
+ implementation('org.grails.plugins:database-migration:4.2.0') {
+ // spring-boot-cli exclusion required since Grails5 upgrade to prevent NullPointerException Error: https://github.com/grails/grails-database-migration/issues/268
+ exclude module: 'spring-boot-cli'
+ }
}
bootRun {
ignoreExitValue true
jvmArgs(
- '-Dspring.output.ansi.enabled=always',
- '-noverify',
- '-XX:TieredStopAtLevel=1',
- '-Xmx1024m')
+ '-Dspring.output.ansi.enabled=always',
+ '-noverify',
+ '-XX:TieredStopAtLevel=1',
+ '-Xmx2048m')
sourceResources sourceSets.main
String springProfilesActive = 'spring.profiles.active'
systemProperty springProfilesActive, System.getProperty(springProfilesActive)
}
-tasks.withType(GroovyCompile) {
- configure(groovyOptions) {
- forkOptions.jvmArgs = ['-Xmx1024m']
- }
-}
-
-webdriverBinaries {
- chromedriver '2.45.0'
- geckodriver '0.24.0'
-}
-
tasks.withType(Test) {
systemProperty "geb.env", System.getProperty('geb.env')
systemProperty "geb.build.reportsDir", reporting.file("geb/integrationTest")
@@ -193,14 +165,17 @@ tasks.withType(Test) {
systemProperty "webdriver.gecko.driver", System.getProperty('webdriver.gecko.driver')
}
+webdriverBinaries {
+ chromedriver "$chromeDriverVersion"
+ geckodriver "$geckodriverVersion"
+}
assets {
minifyJs = true
minifyCss = true
}
-springBoot {
- buildInfo()
+test {
+ testLogging.showStandardStreams = true
}
-
diff --git a/gradle.properties b/gradle.properties
index 903ab8ca..cb00d8fc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,13 @@
-grailsVersion=4.1.1
-gorm.version=7.0.8.RELEASE
+grailsVersion=5.2.1
+gormVersion=7.2.1
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx1024M
+gebVersion=2.3
+groovyVersion=3.0.11
+seleniumVersion=3.14.0
+webdriverBinariesVersion=2.6
+chromeDriverVersion=2.45.0
+geckodriverVersion=0.24.0
+seleniumSafariDriverVersion=3.14.0
+alaSecurityLibsVersion=6.0.4
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 87b738cb..7454180f 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5028f28f..ffed3a25 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index af6708ff..1b6c7873 100755
--- a/gradlew
+++ b/gradlew
@@ -1,78 +1,129 @@
-#!/usr/bin/env sh
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m"'
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -89,84 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=$((i+1))
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 6d57edc7..ac1b06f9 100755
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,84 +1,89 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/grails-app/assets/javascripts/application.js b/grails-app/assets/javascripts/application.js
index 098f6cd1..8084b6f9 100644
--- a/grails-app/assets/javascripts/application.js
+++ b/grails-app/assets/javascripts/application.js
@@ -8,5 +8,6 @@
//= require jquery-3.3.1.min
//= require jquery.i18n.properties-1.0.9.min
//= require jquery.ba-bbq.min
+//= require moment-with-locales.min.js
//= require datasets.js
//= require_self
diff --git a/grails-app/assets/javascripts/datasets.js b/grails-app/assets/javascripts/datasets.js
index 89e44750..73c20223 100644
--- a/grails-app/assets/javascripts/datasets.js
+++ b/grails-app/assets/javascripts/datasets.js
@@ -32,6 +32,9 @@ var allResources;
/* holds current filtered list */
var resources;
+/* holds resources facet for stats */
+var rersourcesStats;
+
/* list of filters currently in effect - items are {name, value} */
var currentFilters = [];
@@ -50,29 +53,50 @@ var biocacheUrl;
/* options for all tooltips */
var tooltipOptions = {position:{my: 'left bottom', at: 'center top-10'}, show: {duration: 130, effect:'fade'}, hide: {duration: 200, effect:'fade'}, collision: "fit"};
+function drCount(dr) {
+ try {
+ return resourcesStats[0].fieldResult.find((result) => result.fq.includes(dr)).count;
+ }
+ catch (e) {
+ return -1;
+ }
+}
+
/** load resources and show first page **/
-function loadResources(serverUrl, biocacheRecordsUrl) {
+function loadResources(serverUrl, biocacheRecordsUrl, biocacheServicesUrl) {
baseUrl = serverUrl;
biocacheUrl = biocacheRecordsUrl;
- $.getJSON(baseUrl + "/public/condensed.json", function(data) {
- allResources = data;
- // no filtering at this stage
- resources = allResources;
-
- setStateFromHash();
- updateTotal();
- calculateFacets();
- showFilters();
- resources.sort(comparator);
- displayPage();
- wireDownloadLink();
- wireSearchLink();
-
- // set up tooltips
- // - don't do download link because the title changes and the tooltip app does not update
- // - also limit to content to exclude links in header
- $('div.collectory-content [title][id!="downloadLink"]');
- });
+
+ function loadResourcesCondensed() {
+ $.getJSON(baseUrl + "/public/condensed.json", function (data) {
+ allResources = data;
+ // no filtering at this stage
+ resources = allResources;
+
+ setStateFromHash();
+ updateTotal();
+ calculateFacets();
+ showFilters();
+ resources.sort(comparator);
+ displayPage();
+ wireDownloadLink();
+ wireSearchLink();
+
+ // set up tooltips
+ // - don't do download link because the title changes and the tooltip app does not update
+ // - also limit to content to exclude links in header
+ $('div.collectory-content [title][id!="downloadLink"]');
+ });
+ }
+
+ if (COLLECTORY_CONF.showExtraInfoInDataSetsView) {
+ $.getJSON(biocacheServicesUrl + "/occurrences/facets?facets=data_resource_uid&pageSize=0&flimit=-1", function(dataStats) {
+ resourcesStats = dataStats;
+ loadResourcesCondensed();
+ });
+ } else {
+ loadResourcesCondensed();
+ }
}
/*************************************************\
* List display
@@ -107,6 +131,14 @@ function appendResource(value) {
var $rowB = $('
').appendTo($div);
var $rowC = $('').appendTo($div); // starts hidden
+ function formatLastUpdated() {
+ if (COLLECTORY_CONF.showExtraInfoInDataSetsViewRelativeTime) {
+ return moment(value.lastUpdated).locale(COLLECTORY_CONF.locale).fromNow();
+ } else {
+ return moment(value.lastUpdated).locale(COLLECTORY_CONF.locale).format("LL");
+ }
+ }
+
// row A
$rowA.append('
'); // twisty
$rowA.append('
' + value.name + ''); // name
@@ -115,8 +147,17 @@ function appendResource(value) {
// row B
$rowB.append('
'+ jQuery.i18n.prop('datasets.js.appendresource06') +': ' + jQuery.i18n.prop('dataset.result.'+ value.resourceType) + ''); // resource type
$rowB.append('
'+ jQuery.i18n.prop('datasets.js.appendresource07') +': ' + (value.licenseType == null ? '' : value.licenseType) + ''); // license type
- if (value.resourceType == 'records') {
+
+ if (COLLECTORY_CONF.showExtraInfoInDataSetsView && value.resourceType == 'records') {
+ $rowB.append('
'+ jQuery.i18n.prop('datasets.js.lastUpdated') +': ' + formatLastUpdated() + ''); // last updated
+ var numRecords = drCount(value.uid);
+ if (numRecords >= 0) {
+ $rowB.append('
' + jQuery.i18n.prop('datasets.js.numRecords') + ': ' + numRecords + ''); // recors link with numbers
+ }
+ }
+ if (!COLLECTORY_CONF.showExtraInfoInDataSetsView && value.resourceType == 'records') {
$rowB.append('
'+ jQuery.i18n.prop('datasets.js.appendresource10') +''); // records link
+
}
if (value.resourceType == 'website' && value.websiteUrl) {
$rowB.append('
'+ jQuery.i18n.prop('datasets.js.appendresource11') +''); // website link
diff --git a/grails-app/assets/javascripts/moment-with-locales.min.js b/grails-app/assets/javascripts/moment-with-locales.min.js
new file mode 100644
index 00000000..fb177050
--- /dev/null
+++ b/grails-app/assets/javascripts/moment-with-locales.min.js
@@ -0,0 +1,2 @@
+!function(e,a){"object"==typeof exports&&"undefined"!=typeof module?module.exports=a():"function"==typeof define&&define.amd?define(a):e.moment=a()}(this,function(){"use strict";var E;function M(){return E.apply(null,arguments)}function F(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function z(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function l(e,a){return Object.prototype.hasOwnProperty.call(e,a)}function N(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(var a in e)if(l(e,a))return;return 1}function d(e){return void 0===e}function J(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function R(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function C(e,a){for(var t=[],s=e.length,n=0;n
>>0,s=0;sBe(e)?(r=e+1,a-Be(e)):(r=e,a);return{year:r,dayOfYear:t}}function Xe(e,a,t){var s,n,r=$e(e.year(),a,t),r=Math.floor((e.dayOfYear()-r-1)/7)+1;return r<1?s=r+ea(n=e.year()-1,a,t):r>ea(e.year(),a,t)?(s=r-ea(e.year(),a,t),n=e.year()+1):(n=e.year(),s=r),{week:s,year:n}}function ea(e,a,t){var s=$e(e,a,t),a=$e(e+1,a,t);return(Be(e)-s+a)/7}s("w",["ww",2],"wo","week"),s("W",["WW",2],"Wo","isoWeek"),a("week","w"),a("isoWeek","W"),t("week",5),t("isoWeek",5),p("w",o),p("ww",o,r),p("W",o),p("WW",o,r),je(["w","ww","W","WW"],function(e,a,t,s){a[s.substr(0,1)]=m(e)});function aa(e,a){return e.slice(a,7).concat(e.slice(0,a))}s("d",0,"do","day"),s("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),s("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),s("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),s("e",0,0,"weekday"),s("E",0,0,"isoWeekday"),a("day","d"),a("weekday","e"),a("isoWeekday","E"),t("day",11),t("weekday",11),t("isoWeekday",11),p("d",o),p("e",o),p("E",o),p("dd",function(e,a){return a.weekdaysMinRegex(e)}),p("ddd",function(e,a){return a.weekdaysShortRegex(e)}),p("dddd",function(e,a){return a.weekdaysRegex(e)}),je(["dd","ddd","dddd"],function(e,a,t,s){s=t._locale.weekdaysParse(e,s,t._strict);null!=s?a.d=s:h(t).invalidWeekday=e}),je(["d","e","E"],function(e,a,t,s){a[s]=m(e)});var ta="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),sa="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),na="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ra=f,da=f,ia=f;function _a(){function e(e,a){return a.length-e.length}for(var a,t,s,n=[],r=[],d=[],i=[],_=0;_<7;_++)s=U([2e3,1]).day(_),a=k(this.weekdaysMin(s,"")),t=k(this.weekdaysShort(s,"")),s=k(this.weekdays(s,"")),n.push(a),r.push(t),d.push(s),i.push(a),i.push(t),i.push(s);n.sort(e),r.sort(e),d.sort(e),i.sort(e),this._weekdaysRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+d.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+r.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+n.join("|")+")","i")}function oa(){return this.hours()%12||12}function ma(e,a){s(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),a)})}function ua(e,a){return a._meridiemParse}s("H",["HH",2],0,"hour"),s("h",["hh",2],0,oa),s("k",["kk",2],0,function(){return this.hours()||24}),s("hmm",0,0,function(){return""+oa.apply(this)+de(this.minutes(),2)}),s("hmmss",0,0,function(){return""+oa.apply(this)+de(this.minutes(),2)+de(this.seconds(),2)}),s("Hmm",0,0,function(){return""+this.hours()+de(this.minutes(),2)}),s("Hmmss",0,0,function(){return""+this.hours()+de(this.minutes(),2)+de(this.seconds(),2)}),ma("a",!0),ma("A",!1),a("hour","h"),t("hour",13),p("a",ua),p("A",ua),p("H",o),p("h",o),p("k",o),p("HH",o,r),p("hh",o,r),p("kk",o,r),p("hmm",c),p("hmmss",L),p("Hmm",c),p("Hmmss",L),D(["H","HH"],w),D(["k","kk"],function(e,a,t){e=m(e);a[w]=24===e?0:e}),D(["a","A"],function(e,a,t){t._isPm=t._locale.isPM(e),t._meridiem=e}),D(["h","hh"],function(e,a,t){a[w]=m(e),h(t).bigHour=!0}),D("hmm",function(e,a,t){var s=e.length-2;a[w]=m(e.substr(0,s)),a[Oe]=m(e.substr(s)),h(t).bigHour=!0}),D("hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[w]=m(e.substr(0,s)),a[Oe]=m(e.substr(s,2)),a[We]=m(e.substr(n)),h(t).bigHour=!0}),D("Hmm",function(e,a,t){var s=e.length-2;a[w]=m(e.substr(0,s)),a[Oe]=m(e.substr(s))}),D("Hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[w]=m(e.substr(0,s)),a[Oe]=m(e.substr(s,2)),a[We]=m(e.substr(n))});f=Ye("Hours",!0);var la,Ma={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ne,monthsShort:Je,week:{dow:0,doy:6},weekdays:ta,weekdaysMin:na,weekdaysShort:sa,meridiemParse:/[ap]\.?m?\.?/i},b={},ha={};function ca(e){return e&&e.toLowerCase().replace("_","-")}function La(e){for(var a,t,s,n,r=0;r=a&&function(e,a){for(var t=Math.min(e.length,a.length),s=0;s=a-1)break;a--}r++}return la}function Ya(a){var e;if(void 0===b[a]&&"undefined"!=typeof module&&module&&module.exports&&null!=a.match("^[^/\\\\]*$"))try{e=la._abbr,require("./locale/"+a),ya(e)}catch(e){b[a]=null}return b[a]}function ya(e,a){return e&&((a=d(a)?pa(e):fa(e,a))?la=a:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),la._abbr}function fa(e,a){if(null===a)return delete b[e],null;var t,s=Ma;if(a.abbr=e,null!=b[e])ae("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=b[e]._config;else if(null!=a.parentLocale)if(null!=b[a.parentLocale])s=b[a.parentLocale]._config;else{if(null==(t=Ya(a.parentLocale)))return ha[a.parentLocale]||(ha[a.parentLocale]=[]),ha[a.parentLocale].push({name:e,config:a}),null;s=t._config}return b[e]=new ne(se(s,a)),ha[e]&&ha[e].forEach(function(e){fa(e.name,e.config)}),ya(e),b[e]}function pa(e){var a;if(!(e=e&&e._locale&&e._locale._abbr?e._locale._abbr:e))return la;if(!F(e)){if(a=Ya(e))return a;e=[e]}return La(e)}function ka(e){var a=e._a;return a&&-2===h(e).overflow&&(a=a[xe]<0||11ze(a[g],a[xe])?Pe:a[w]<0||24ea(r,_,o)?h(s)._overflowWeeks=!0:null!=m?h(s)._overflowWeekday=!0:(u=Qe(r,d,i,_,o),s._a[g]=u.year,s._dayOfYear=u.dayOfYear)),null!=e._dayOfYear&&(n=Oa(e._a[g],t[g]),(e._dayOfYear>Be(n)||0===e._dayOfYear)&&(h(e)._overflowDayOfYear=!0),m=Ze(n,0,e._dayOfYear),e._a[xe]=m.getUTCMonth(),e._a[Pe]=m.getUTCDate()),a=0;a<3&&null==e._a[a];++a)e._a[a]=l[a]=t[a];for(;a<7;a++)e._a[a]=l[a]=null==e._a[a]?2===a?1:0:e._a[a];24===e._a[w]&&0===e._a[Oe]&&0===e._a[We]&&0===e._a[Ae]&&(e._nextDay=!0,e._a[w]=0),e._d=(e._useUTC?Ze:qe).apply(null,l),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[w]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(h(e).weekdayMismatch=!0)}}function Aa(e){if(e._f===M.ISO_8601)ja(e);else if(e._f===M.RFC_2822)Pa(e);else{e._a=[],h(e).empty=!0;for(var a,t,s,n,r,d=""+e._i,i=d.length,_=0,o=le(e._f,e._locale).match(ie)||[],m=o.length,u=0;ue.valueOf():e.valueOf()"}),n.toJSON=function(){return this.isValid()?this.toISOString():null},n.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},n.unix=function(){return Math.floor(this.valueOf()/1e3)},n.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},n.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},n.eraName=function(){for(var e,a=this.localeData().eras(),t=0,s=a.length;tthis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},n.isLocal=function(){return!!this.isValid()&&!this._isUTC},n.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},n.isUtc=Ka,n.isUTC=Ka,n.zoneAbbr=function(){return this._isUTC?"UTC":""},n.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},n.dates=e("dates accessor is deprecated. Use date instead.",Y),n.months=e("months accessor is deprecated. Use month instead",Ge),n.years=e("years accessor is deprecated. Use year instead",Ke),n.zone=e("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,a){return null!=e?(this.utcOffset(e="string"!=typeof e?-e:e,a),this):-this.utcOffset()}),n.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!d(this._isDSTShifted))return this._isDSTShifted;var e,a={};return Z(a,this),(a=Ea(a))._a?(e=(a._isUTC?U:S)(a._a),this._isDSTShifted=this.isValid()&&0
+
+
+
+
+
+
+
+ UTF-8
+ %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/grails-app/controllers/au/org/ala/collectory/CollectoryWebServicesInterceptor.groovy b/grails-app/controllers/au/org/ala/collectory/CollectoryWebServicesInterceptor.groovy
index 9dd94bbd..736e31b4 100644
--- a/grails-app/controllers/au/org/ala/collectory/CollectoryWebServicesInterceptor.groovy
+++ b/grails-app/controllers/au/org/ala/collectory/CollectoryWebServicesInterceptor.groovy
@@ -34,19 +34,19 @@ class CollectoryWebServicesInterceptor {
}
boolean before() {
// set default role requirement for protected ROLE_EDITOR as the same info is only available to ROLE_EDITOR via the UI.
- String[] requiredRoles = [grailsApplication.config.ROLE_EDITOR]
+ String requiredRole = grailsApplication.config.ROLE_EDITOR
// set gbifRegistrationRole role requirement for GBIF operations
if(controllerName == 'gbif' || actionName == 'syncGBIF'){
- requiredRoles = [grailsApplication.config.gbifRegistrationRole]
+ requiredRole = grailsApplication.config.gbifRegistrationRole
}
// set ROLE_ADMIN role requirement for certain controllers and actions as per admin UI
if( controllerName == 'ipt'){
- requiredRoles = [grailsApplication.config.ROLE_ADMIN]
+ requiredRole = grailsApplication.config.ROLE_ADMIN
}
- if (collectoryAuthService.isAuthorisedWsRequest(params, request, response, requiredRoles, null)) {
+ if (collectoryAuthService.isAuthorisedWsRequest(params, request, response, requiredRole, null)) {
return true
}
log.warn("Denying access to $actionName from remote addr: ${request.remoteAddr}, remote host: ${request.remoteHost}")
diff --git a/grails-app/controllers/au/org/ala/collectory/DataController.groovy b/grails-app/controllers/au/org/ala/collectory/DataController.groovy
index bf8e3d6b..cb2c0aac 100644
--- a/grails-app/controllers/au/org/ala/collectory/DataController.groovy
+++ b/grails-app/controllers/au/org/ala/collectory/DataController.groovy
@@ -449,7 +449,7 @@ class DataController {
def entityInJson
if (clazz == 'DataResource') {
// this auth check (JWT or API key) is a special case handling to support backwards compatibility(which used to check for API key).
- String [] requiredRoles = [grailsApplication.config.ROLE_ADMIN]
+ String requiredRoles = grailsApplication.config.ROLE_ADMIN
def authCheck = collectoryAuthService.isAuthorisedWsRequest(getParams(), request, response, requiredRoles,null)
entityInJson = crudService."read${clazz}"(params.pg, authCheck)
} else {
@@ -463,7 +463,12 @@ class DataController {
// return list of entities
addContentLocation "/ws/${urlForm}"
def domain = grailsApplication.getClassForName("au.org.ala.collectory.${clazz}")
- def list = domain.list([sort:'name'])
+ def list = []
+ if (clazz == 'DataResource') {
+ list = domain.findAllByIsPrivate(false, [sort: 'name'])
+ } else {
+ list = domain.list([sort: 'name'])
+ }
list = filter(list)
def last = latestModified(list)
def detail = params.summary ? summary : brief
@@ -500,7 +505,7 @@ class DataController {
// suppress 'declined' data resources
if (urlForm == 'dataResource' && params.public == "true") {
- list = list.findAll { it.status != 'declined' }
+ list = list.findAll { it.status != 'declined' && it.isPrivate == false }
}
// init results with total
@@ -553,9 +558,9 @@ class DataController {
@Path("/ws/syncGBIF")
@Produces("application/json")
def syncGBIF () {
- asyncGbifRegistryService.updateAllRegistrations()
- .onComplete { List results ->
- log.error "Provider synced = ${results.size()}"
+ asyncGbifRegistryService.updateAllResources()
+ .onComplete {
+ log.info "Sync complete"
}
.onError { Throwable err ->
log.error("An error occured ${err.message}", err)
@@ -1098,7 +1103,12 @@ class DataController {
*/
def contactsForEntity = {
check(params)
- def contactList = params.pg.getContacts().collect { buildContactForModel(it, params.pg.urlForm()) }
+ def contactList = params.pg.getContacts().collect {
+ // public contacts only
+ if (it.contact.publish) {
+ buildContactForModel(it, params.pg.urlForm())
+ }
+ }.findAll { it != null }
addContentLocation "/ws/${params.entity}/${params.pg.uid}/contacts"
addVaryAcceptHeader()
withFormat {
@@ -1130,7 +1140,8 @@ class DataController {
Contact contact = Contact.get(params.id)
ContactFor contactFor = ContactFor.findByContact(contact)
- if (contact && contactFor) {
+ // public contacts only
+ if (contact?.publish && contactFor) {
def cm = buildContactForModel(contactFor, params.pg.urlForm())
addContentLocation "/ws/${params.entity}/${params.pg.uid}/contacts/${params.id}"
addVaryAcceptHeader()
diff --git a/grails-app/controllers/au/org/ala/collectory/MessagesController.groovy b/grails-app/controllers/au/org/ala/collectory/MessagesController.groovy
index 2af38192..526abc80 100644
--- a/grails-app/controllers/au/org/ala/collectory/MessagesController.groovy
+++ b/grails-app/controllers/au/org/ala/collectory/MessagesController.groovy
@@ -1,5 +1,7 @@
package au.org.ala.collectory
+import org.springframework.web.servlet.support.RequestContextUtils
+
class MessagesController {
def messageSource
@@ -13,7 +15,7 @@ class MessagesController {
* @return
*/
def i18n(String id) {
- def locale = org.springframework.web.servlet.support.RequestContextUtils.getLocale(request) ?: request.locale
+ def locale = RequestContextUtils.getLocale(request) ?: request.locale
def keys = messageSource.withTraits(MessagePropertiesTrait).getMessageKeys(locale)
response.setHeader("Content-type", "text/plain; charset=UTF-8")
def messages = keys.collect { "${it.key}=${it.value}" }
diff --git a/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy b/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy
index 2226b2dd..510ad193 100644
--- a/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy
+++ b/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy
@@ -5,7 +5,9 @@ import grails.converters.JSON
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.multipart.MultipartFile
-
+import java.text.NumberFormat
+import java.text.ParseException
+import org.springframework.web.context.request.ServletRequestAttributes
/**
* This is a base class for all provider group entities types.
*
@@ -290,9 +292,19 @@ abstract class ProviderGroupController {
if (pg) {
if (checkLocking(pg,'/shared/location')) { return }
- // special handling for lat & long
- if (!params.latitude) { params.latitude = -1 }
- if (!params.longitude) { params.longitude = -1 }
+ Locale userLocale = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request.locale
+ NumberFormat numberFormat = NumberFormat.getNumberInstance(userLocale)
+
+ double latitude
+ double longitude
+
+ try {
+ latitude = params.latitude ? numberFormat.parse(params.latitude).doubleValue() : -1
+ longitude = params.longitude ? numberFormat.parse(params.longitude).doubleValue() : -1
+ } catch (ParseException e) {
+ latitude = -1
+ longitude = -1
+ }
// special handling for embedded address - need to create address obj if none exists and we have data
if (!pg.address && [params.address?.street, params.address?.postBox, params.address?.city,
@@ -301,6 +313,8 @@ abstract class ProviderGroupController {
}
pg.properties = params
+ pg.latitude = latitude
+ pg.longitude = longitude
pg.userLastModified = collectoryAuthService?.username()
if (!pg.hasErrors() ) {
diff --git a/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy b/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy
index 80440c8d..08328530 100644
--- a/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy
+++ b/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy
@@ -48,9 +48,14 @@ class ProviderMapController {
@Transactional
def save () {
def providerMapInstance = new ProviderMap(params)
+ if (providerMapInstance.collection && providerMapInstance.collection.providerMap){
+ flash.message = "${message(code: 'providerMap.collection.used.label', args: [providerMapInstance.collection.uid])}"
+ render(view: "create", model: [providerMapInstance: providerMapInstance, returnTo: params.returnTo])
+ return
+ }
if (providerMapInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'providerMap.label', default: 'ProviderMap'), providerMapInstance.id])}"
- redirect(action: "show", id: providerMapInstance.id, params:[returnTo: params.returnTo])
+ render(view: "create", model: [providerMapInstance: providerMapInstance, returnTo: params.returnTo])
}
else {
render(view: "create", model: [providerMapInstance: providerMapInstance, returnTo: params.returnTo])
diff --git a/grails-app/controllers/au/org/ala/collectory/PublicController.groovy b/grails-app/controllers/au/org/ala/collectory/PublicController.groovy
index f2aca926..85f7f65b 100644
--- a/grails-app/controllers/au/org/ala/collectory/PublicController.groovy
+++ b/grails-app/controllers/au/org/ala/collectory/PublicController.groovy
@@ -464,6 +464,9 @@ class PublicController {
else if (instance.status == "declined") {
render "This resource has decided to not contribute to the Atlas."
}
+ else if (instance.isPrivate) {
+ render "This resource is private."
+ }
else {
def suitableFor = providerGroupService.getSuitableFor()
activityLogService.log collectoryAuthService?.username(), collectoryAuthService?.userInRole(grailsApplication.config.ROLE_ADMIN), instance.uid, Action.VIEW
@@ -527,7 +530,7 @@ class PublicController {
def dataSets = {}
def dataSetSearch = {
- def drs = DataResource.findAllByNameLikeAndStatusNotEqual('%' + params.q + '%', 'declined').collect {
+ def drs = DataResource.findAllByNameLikeAndStatusNotEqualAndIsPrivate('%' + params.q + '%', 'declined', false).collect {
it.uid
}
render drs as JSON
@@ -535,7 +538,7 @@ class PublicController {
def resources = {
cache shared:true, validFor: 3600*24
- def drs = DataResource.findAllByStatusNotEqual('declined', [sort:'name']).collect {
+ def drs = DataResource.findAllByStatusNotEqualAndIsPrivate('declined', false, [sort:'name']).collect {
//def drs = DataResource.list([sort:'name']).collect {
def pdesc = it.pubDescription ? cl.formattedText(dummy:'1',limit(it.pubDescription,1000)) : ""
def tdesc = it.techDescription ? cl.formattedText(dummy:'1',limit(it.techDescription,1000)) : ""
@@ -551,13 +554,13 @@ class PublicController {
}
def condensed = {
- def drs = DataResource.findAllByStatusNotEqual('declined', [sort:'name'] ).collect {
+ def drs = DataResource.findAllByStatusNotEqualAndIsPrivate('declined', false, [sort:'name'] ).collect {
//def drs = DataResource.list([sort:'name']).collect {
def inst = it.institution
def instName = (inst && inst.name.size() > 36 && inst.acronym) ? inst.acronym : inst?.name
[name: it.name, resourceType: it.resourceType, licenseType: it.licenseType,
uid: it.uid, status: it.status, websiteUrl: it.websiteUrl, contentTypes: it.contentTypes,
- institution: instName]
+ institution: instName, lastUpdated: it.lastUpdated]
}
render drs as JSON
}
diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties
index cb326532..833c31f4 100644
--- a/grails-app/i18n/messages.properties
+++ b/grails-app/i18n/messages.properties
@@ -22,7 +22,7 @@ public.chart.radio.onf=Other named facet
public.chart.radio.el=Environmental layer
public.datasets.title=Datasets
-public.datasets.header.title.param={0} data sets
+public.datasets.header.title.param={0} datasets
public.datasets.header.message.param=Much of the content in the {0}, such as occurrence records, environmental data, images and the conservation status of species, comes from data sets provided by collecting institutions, individual collectors and community groups. These data sets are listed on this page.
public.datasets.header.message03=Refine the list of data sets by clicking on a filter in the left hand list. Click the
public.datasets.header.message04=toggle to see a description of the data set. To find out more, go to the data set's full metadata page by clicking on its name
@@ -99,7 +99,7 @@ public.purpose=Purpose
public.geographicDescription = Geographic Description
public.methodStepDescription = Methods
public.location=Location
-public.website=Web site
+public.website=Website
public.numbrs.des01=Looking up... the number of records that
public.numbrs.des02=can be accessed through the {0}
public.numbrs.link=Click to view records for the
@@ -198,8 +198,10 @@ public.sdr.content.link01=Click to view records for the
public.sdr.content.link02=resource
public.sdr.content.label10=Map of records
public.sdr.content.label12=Species lists
+public.sdr.content.label13=Publication details
public.sdr.content.link03=View this species list in species list tool
public.sdr.content.link04=Visit the data resource's website
+public.sdr.content.link05=Visit the publication's website
public.sdr.infosourcelist.title=Contributors to this page
public.sdr.externalIdentifiers.title=Other Identifiers
@@ -526,6 +528,7 @@ collection.acronym.label=Acronym
collection.resourceType.label=Resource type
collection.institution.label=Institution
collection.gbifDoi.label=DOI
+collection.isPrivate.label=Is private ?
dataprovider.gbif.country=GBIF Attribution
dataResource.dataProvider.label=Data provider
@@ -1091,6 +1094,7 @@ dataresource.show.sor=Source of records
dataresource.show.guid=GUID
dataresource.show.doi=DOI
dataresource.show.resourcetype=Resource type
+dataresource.show.isprivate=Is private
dataresource.show.website=Website
dataresource.show.dg=Data generalisations
dataresource.show.iw=Information withheld
@@ -1467,6 +1471,7 @@ providerMap.dateCreated.label=Date Created
providerMap.providerGroup.label=Provider Group
providerMap.institution.label=Institution
providerMap.collection.label=Collection
+providerMap.collection.used.label= Unable to create or update ProviderMap. Collection {0} is already in use with another ProviderMap
#The following messages are for the Web Service page. They are not urgent to translate
@@ -1710,7 +1715,6 @@ licence.url.list.label=URL
licence.acronym.list.label=Acronym
licence.version.list.label=Version
licence.image.list.label=Image
-licence.name.label=Name
licence.acronym.label=Acronym
licence.version.label=Version
licence.url.label=URL
@@ -1862,6 +1866,7 @@ metadata.last.updated=Metadata last updated on
dataAccess.title=Data access
dataAccess.view.records=View records
+dataAccess.view.annotated.records=View referenced records
dataAccess.download.stats=Download usage stats
dataAccess.download.records=Download records
dataAccess.alert.records=Create an email alert for new records for
@@ -2072,6 +2077,8 @@ datasets.js.facets07=The types of information available in the data set. A set m
datasets.js.facets08=Contains
datasets.js.facets09=Institution
datasets.js.facets10=The organisation or institution that is the source or custodian of the data set
+datasets.js.lastUpdated=Last updated
+datasets.js.numRecords=Number of records
provider.group.controller.01=You are not authorised to access this page. You do not have 'Collection editor' rights.
provider.group.controller.02=Another user has updated this
@@ -2248,6 +2255,9 @@ dataset.result.records = records
dataset.result.species-list = species-list
dataset.result.website = website
dataset.result.document = document
+dataset.result.annotations = annotations
+dataset.result.events = events
+dataset.result.publications = publication
public.datasets.search.btn.title=Only show data sets which contain the search term
citations.available = citations for these data
collectory.tag.lib.the.collection.was.established.in=The collection was established in {0} and ceased acquisitions in {1}.
@@ -2285,4 +2295,22 @@ collectory.tag.lib.states.covered.include=States covered include: {0}.
public.show.collection.has.an.estimated=has an estimated {0} {1}
public.show.percent.over=over 100
images.available.count={0} images
-images.available.count.available=have been made available from the {0}.
\ No newline at end of file
+images.available.count.available=have been made available from the {0}.
+
+resourceType.publications.list=Publications
+resourceType.annotations.list=Annotations
+resourceType.records.list=Occurrences
+resourceType.events.list=Events
+resourceType.species-list.list=Species lists
+
+resourceType.publications=Publication
+resourceType.annotations=Annotations dataset
+resourceType.records=Occurrence dataset
+resourceType.events=Events based data
+resourceType.species-list=Species list
+
+resourceType.publications.description=This publication references data from other datasets (occurrence or event based datasets). It may also include some modifications to the data as it is presented by the Atlas.
+resourceType.annotations.description=This dataset references data from other datasets (occurrence or event based datasets). It may also include some modifications to the data as it is presented by the Atlas.
+resourceType.records.description=This dataset that contains primary occurrence data for species.
+resourceType.events.description=This dataset that contains event based data for species which may include sampling information
+resourceType.species-list.description=This dataset contains a list of species with an associated trait.
diff --git a/grails-app/migrations/changelog.xml b/grails-app/migrations/changelog.xml
index 230a3557..0ab6370f 100644
--- a/grails-app/migrations/changelog.xml
+++ b/grails-app/migrations/changelog.xml
@@ -1,5 +1,5 @@
-
+
@@ -34,6 +33,7 @@
+
Is private |
Licence |
Verified |
@@ -54,6 +54,9 @@
${fieldValue(bean: instance, field: "resourceType")} |
+
+ ${instance.isPrivate}
+ |
${instance.licenseType}
|
diff --git a/grails-app/views/dataResource/show.gsp b/grails-app/views/dataResource/show.gsp
index 5c942b66..485db050 100644
--- a/grails-app/views/dataResource/show.gsp
+++ b/grails-app/views/dataResource/show.gsp
@@ -34,7 +34,9 @@
-
+
+
+
@@ -76,6 +78,10 @@
: ${fieldValue(bean: instance, field: "resourceType")}
+
+ : ${fieldValue(bean: instance, field: "isPrivate")}
+
+
:
diff --git a/grails-app/views/providerMap/create.gsp b/grails-app/views/providerMap/create.gsp
index 9f8250c1..c69776c2 100644
--- a/grails-app/views/providerMap/create.gsp
+++ b/grails-app/views/providerMap/create.gsp
@@ -15,7 +15,7 @@
- ${flash.message}
+ ${flash.message}
diff --git a/grails-app/views/public/_dataLinks.gsp b/grails-app/views/public/_dataLinks.gsp
new file mode 100644
index 00000000..4e03b847
--- /dev/null
+++ b/grails-app/views/public/_dataLinks.gsp
@@ -0,0 +1,8 @@
+<%@ page import="au.org.ala.collectory.CollectoryTagLib" %>
+
\ No newline at end of file
diff --git a/grails-app/views/public/dataSets.gsp b/grails-app/views/public/dataSets.gsp
index bc9ab46f..d79eaf9e 100644
--- a/grails-app/views/public/dataSets.gsp
+++ b/grails-app/views/public/dataSets.gsp
@@ -8,7 +8,9 @@
var COLLECTORY_CONF = {
contextPath: "${request.contextPath}",
locale: "${(org.springframework.web.servlet.support.RequestContextUtils.getLocale(request).toString())?:request.locale}",
- cartodbPattern: "${grailsApplication.config.cartodb.pattern}"
+ cartodbPattern: "${grailsApplication.config.cartodb.pattern}",
+ showExtraInfoInDataSetsView: ${grailsApplication.config.getProperty("showExtraInfoInDataSetsView.enabled", Boolean.class)},
+ showExtraInfoInDataSetsViewRelativeTime: ${grailsApplication.config.getProperty("showExtraInfoInDataSetsView.relativeTime", Boolean.class)}
};
var CHARTS_CONFIG = {
biocacheServicesUrl: "https://biocache-ws.ala.org.au/ws",
@@ -116,7 +118,7 @@
var altMap = true;
$(document).ready(function() {
- loadResources("${grailsApplication.config.grails.serverURL}","${grailsApplication.config.biocacheUiURL}");
+ loadResources("${grailsApplication.config.grails.serverURL}","${grailsApplication.config.biocacheUiURL}","${grailsApplication.config.biocacheServicesUrl}");
$('select#per-page').change(onPageSizeChange);
$('select#sort').change(onSortChange);
$('select#dir').change(onDirChange);
diff --git a/grails-app/views/public/showDataProvider.gsp b/grails-app/views/public/showDataProvider.gsp
index 8e40bec8..fa2bb554 100644
--- a/grails-app/views/public/showDataProvider.gsp
+++ b/grails-app/views/public/showDataProvider.gsp
@@ -68,7 +68,7 @@
-
+
- ${c?.name}
${c?.makeAbstract(400)}
diff --git a/grails-app/views/public/showDataResource.gsp b/grails-app/views/public/showDataResource.gsp
index 36f1d624..936d1c8e 100644
--- a/grails-app/views/public/showDataResource.gsp
+++ b/grails-app/views/public/showDataResource.gsp
@@ -7,6 +7,9 @@
+
${fieldValue(bean: instance, field: "name")}