diff --git a/README.md b/README.md index da4009f2..56cc1aa0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # DigiVol [![Build Status](https://travis-ci.org/AtlasOfLivingAustralia/volunteer-portal.svg?branch=develop)](https://travis-ci.org/AtlasOfLivingAustralia/volunteer-portal) The [Atlas of Living Australia], in collaboration with the [Australian Museum], developed [DigiVol] -to harness the power of online volunteers (also known as crowdsourcing) to digitise biodiversity data that is locked up -in biodiversity collections, field notebooks and survey sheets. +to harness the power of online volunteers (also known as crowdsourcing) to digitise biodiversity data that is locked +up in biodiversity collections, field notebooks and survey sheets. ## Running @@ -33,7 +33,7 @@ ansible-playbook -i inventories/vagrant --user vagrant --private-key ~/.vagrant. ## Contributing -DigiVol is a [Grails] v3.2.4 based web application. It requires [PostgreSQL] for data storage. Development follows the +DigiVol is a [Grails] v5.3 based web application. It requires [PostgreSQL] v15 for data storage. Development follows the [git flow] workflow. For git flow operations you may like to use the `git-flow` command line tools. Either install [Atlassian SourceTree] diff --git a/build.gradle b/build.gradle index 29ecddb8..505ea347 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { id "com.dorongold.task-tree" version "2.1.1" } -version "6.1.7" +version "6.1.8" group "au.org.ala" description "Digivol application" diff --git a/gradle.properties b/gradle.properties index eee60b5a..9b8dc973 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ springBootVersion=2.7.9 org.gradle.daemon=true org.gradle.parallel=true org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx1024M -alaAuthVersion=6.0.3 +alaAuthVersion=6.2.0 #grailsWrapperVersion=1.0.0 gradleWrapperVersion=5.0 diff --git a/grails-app/assets/javascripts/transcribe/wildlifespotter.js b/grails-app/assets/javascripts/transcribe/wildlifespotter.js index f2d4ae08..dcb64066 100644 --- a/grails-app/assets/javascripts/transcribe/wildlifespotter.js +++ b/grails-app/assets/javascripts/transcribe/wildlifespotter.js @@ -18,11 +18,15 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { var selectedIndicies = {}; + // Default save button to disabled until a selection has been made. + $('#btnSave').attr('disabled', 'disabled'); + // selection $('#ct-container').on('click', '.ws-selector', function() { var $this = $(this); var index = $this.closest('[data-item-index]').data('item-index'); - toggleIndex(index); + var validationtype = $this.data('validationType'); + toggleIndex(index, validationtype); }); $('#ct-container').on('click', '.animalDelete', function() { @@ -31,16 +35,36 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { deselectIndex(index); }); - function toggleIndex(index) { + $('input[name=recordValues\\.0\\.noAnimalsVisible]').change(function() { + checkCheckboxValues(); + }); + + $('input[name=recordValues\\.0\\.problemWithImage]').change(function() { + checkCheckboxValues(); + }); + + function checkCheckboxValues() { + var q1 = $('input[name=recordValues\\.0\\.noAnimalsVisible]:checked').val(); + var q2 = $('input[name=recordValues\\.0\\.problemWithImage]:checked').val(); + if (!q1 && !q2) { + $('#btnSave').attr('disabled', 'disabled'); + } else { + $('#btnSave').removeAttr('disabled'); + } + } + + function toggleIndex(index, validationType = "speciesWithCount") { if (selectedIndicies.hasOwnProperty(index)) { deselectIndex(index); } else { - selectIndex(index); + selectIndex(index, validationType); } } - function selectIndex(index) { - selectedIndicies[index] = { count: 1, notes: '', editorOpen: false}; + function selectIndex(index, validationType = "speciesWithCount") { + var count = 0; + if (validationType === "speciesOnly") count = 1; + selectedIndicies[index] = { count: count, notes: '', editorOpen: false, init: true}; syncSelections(); } @@ -50,7 +74,9 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { } function syncSelections() { - var usKeys = _.chain(selectedIndicies).keys().filter(function(idx) { return selectedIndicies[idx].count > 0; }); + var usKeys = _.chain(selectedIndicies).keys().filter(function(idx) { + return selectedIndicies[idx].count >= 0; + }); var dataItemIndexes = usKeys.map(function(v,i,l) { return "[data-item-index='"+v+"']"}); var wsSelectionIndicator = dataItemIndexes.map(function(v,i,l) { return v + " .ws-selected"; }); var wsSelectorIndicator = dataItemIndexes.map(function(v,i,l) { return v + " .ws-selector"; }); @@ -58,6 +84,7 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { $(wsSelectorIndicator.value().join(", ")).attr('aria-selected', 'true'); $('[data-item-index]:not('+ dataItemIndexes.value().join(',') + ') .ws-selected').removeClass('selected').attr('aria-selected', 'false'); $('[data-item-index]:not('+ dataItemIndexes.value().join(',') + ') .ws-selector').attr('aria-selected', 'false'); + var length = usKeys.value().length; if (length == 0) { hideSelectionPanel(); @@ -83,17 +110,13 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { return { index: v, name: wsParams.animals[v].vernacularName, - options: _([1,2,3,4,5,6,7,8,9,10]).map(function(opt,i) { - return { - val: opt, - selected: selectedIndicies[v].count == opt ? 'selected' : '', - isSelected: selectedIndicies[v].count == opt ? 'true' : 'false' - }; - }), + curval: selectedIndicies[v].count, comment: selectedIndicies[v].comment }; + }).sortBy(function(o) { return o.index; }).value() }; + mu.replaceTemplate(parent, 'status-detail-list-template', templateObj); parent.show(); @@ -110,14 +133,20 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { generateFormFields(); }); - $('#ct-container').on('change', 'select.numAnimals', function() { + //$('#ct-container').on('change', 'select.numAnimals', function() { + $('#ct-container').on('change', 'input.numAnimals', function() { var $this = $(this); var idx = $this.closest('[data-item-index]').data('item-index'); var count = $this.val(); + // console.log("value change: " + count); selectedIndicies[idx].count = parseInt(count); generateFormFields(); }); + $('.input-group-btn-vertical').click(function() { + $('#ct-container').trigger("change"); + }); + $('#ct-container').on('click', '.editCommentButton', function() { var $this = $(this); var idx = $this.closest('[data-item-index]').data('item-index'); @@ -128,6 +157,19 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { selectedIndicies[idx].editorOpen = true; }); + $("#ct-container").on('keydown', '.numAnimals', function(e) { + // Allow: backspace, delete, tab, escape, enter and . + if ($.inArray(e.keyCode, [46, 8, 9, 27, 13, 110, 190]) !== -1 || (e.keyCode === 65 && e.ctrlKey === true) || (e.keyCode >= 35 && e.keyCode <= 40)) { + // console.log("key " + e.keyCode + " allowed"); + return; + } + + if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) { + // console.log("key " + e.keyCode + " not allowed"); + e.preventDefault(); + } + }); + $('#ct-container').on('click', '.saveCommentButton', function() { var $this = $(this); var idx = $this.closest('[data-item-index]').data('item-index'); @@ -313,6 +355,7 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { function generateFormFields() { var $ctFields = $('#ct-fields'); $ctFields.empty(); + var enableSubmit = true; if (_.keys(selectedIndicies).length > 0) { $('input[name=recordValues\\.0\\.noAnimalsVisible]').removeAttr('checked'); $('input[name=recordValues\\.0\\.problemWithImage]').removeAttr('checked'); @@ -320,12 +363,19 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { delete recordValues[0].noAnimalsVisible; delete recordValues[0].problemWithImage; } + _.each(selectedIndicies, function(value, key, list) { + if (value.count === 0) enableSubmit = false; + }); } else { if (recordValues && recordValues[0]) { delete recordValues[0].vernacularName; delete recordValues[0].scientificName; delete recordValues[0].individualCount; } + + var q1 = $('input[name=recordValues\\.0\\.noAnimalsVisible]:checked').val(); + var q2 = $('input[name=recordValues\\.0\\.problemWithImage]:checked').val(); + if (!q1 && !q2) enableSubmit = false; } var i = 0; _.each(selectedIndicies, function (value, key, list) { @@ -335,6 +385,10 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { mu.appendTemplate($ctFields, 'input-template', {id: 'recordValues.' + i + '.comment', value: value.comment}); ++i; }); + + // console.log("Enable submit button? " + enableSubmit); + if (enableSubmit) $('#btnSave').removeAttr('disabled'); + else $('#btnSave').attr('disabled', 'disabled'); } function syncRecordValues() { @@ -371,12 +425,10 @@ function wildlifespotter(wsParams, imagePrefix, recordValues, placeholders) { errorList.push({element: null, message: "You must either indicate that there are no animals, there's a problem with the image or select at least one animal before you can submit", type: "Error" }); } }); - transcribeValidation.setErrorRenderFunctions(function (errorList) { - }, - function() { - }); - submitRequiresConfirmation = true; + transcribeValidation.setErrorRenderFunctions(function (errorList) {}, function() {}); + var submitRequiresConfirmation = true; + postValidationFunction = function(validationResults) { if (validationResults.errorList.length > 0) bootbox.alert("

Invalid selection