From a6bb7111e638ade4a0869c3a2502cded2cf5d131 Mon Sep 17 00:00:00 2001 From: Tor-Einar Skog <tor-einar.skog@nibio.no> Date: Fri, 17 Mar 2017 17:48:03 -0700 Subject: [PATCH] Changes in observation form and list. Adding shortcuts to specific registration types --- pom.xml | 18 +- .../servlet/ObservationController.java | 39 +++- .../controller/session/ObservationBean.java | 7 + .../controller/session/OrganismBean.java | 50 ++++- .../no/nibio/vips/logic/entity/CropPest.java | 1 + .../nibio/vips/logic/entity/Observation.java | 16 ++ .../logic/entity/ObservationFormShortcut.java | 176 ++++++++++++++++++ .../entity/ObservationFormShortcutLocale.java | 118 ++++++++++++ .../vips/logic/service/VIPSMobileService.java | 4 +- .../vips/logic/i18n/vipslogictexts.properties | 5 + .../logic/i18n/vipslogictexts_bs.properties | 5 + .../logic/i18n/vipslogictexts_hr.properties | 5 + .../logic/i18n/vipslogictexts_nb.properties | 5 + .../logic/i18n/vipslogictexts_sr.properties | 5 + .../i18n/vipslogictexts_zh_CN.properties | 5 + .../formdefinitions/observationForm.json | 5 + src/main/webapp/templates/observationForm.ftl | 141 +++++++------- src/main/webapp/templates/observationList.ftl | 13 ++ 18 files changed, 542 insertions(+), 76 deletions(-) create mode 100644 src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcut.java create mode 100644 src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcutLocale.java diff --git a/pom.xml b/pom.xml index a9064c24..3d51165f 100755 --- a/pom.xml +++ b/pom.xml @@ -16,10 +16,9 @@ <repositories> <repository> - <id>jitpack.io</id> - <url>https://jitpack.io</url> -</repository> - + <id>jitpack.io</id> + <url>https://jitpack.io</url> + </repository> </repositories> <dependencies> <dependency> @@ -63,6 +62,16 @@ <artifactId>VIPSCommon</artifactId> <version>1.0-SNAPSHOT</version> <type>jar</type> + <exclusions> + <exclusion> + <groupId>org.python</groupId> + <artifactId>jython-standalone</artifactId> + </exclusion> + <exclusion> + <groupId>org.renjin</groupId> + <artifactId>renjin-script-engine</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.reflections</groupId> @@ -201,6 +210,7 @@ <version>9.4-1211</version> <scope>provided</scope> </dependency--> + </dependencies> <build> diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java index ed20e136..15cf47d3 100755 --- a/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java +++ b/src/main/java/no/nibio/vips/logic/controller/servlet/ObservationController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 NIBIO <http://www.nibio.no/>. + * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. * * This file is part of VIPSLogic. * VIPSLogic is free software: you can redistribute it and/or modify @@ -36,8 +36,8 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import no.nibio.vips.logic.entity.Observation; +import no.nibio.vips.logic.entity.ObservationFormShortcut; import no.nibio.vips.logic.entity.ObservationMethod; -import no.nibio.vips.logic.entity.ObservationStatusType; import no.nibio.vips.logic.entity.Organism; import no.nibio.vips.logic.entity.Organization; import no.nibio.vips.logic.entity.VipsLogicRole; @@ -58,7 +58,7 @@ import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; /** - * @copyright 2014 <a href="http://www.nibio.no/">NIBIO</a> + * @copyright 2014-2017 <a href="http://www.nibio.no/">NIBIO</a> * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ public class ObservationController extends HttpServlet { @@ -127,9 +127,13 @@ public class ObservationController extends HttpServlet { .collect(Collectors.toList()); } + // Check for form shortcuts + List<ObservationFormShortcut> shortcuts = SessionControllerGetter.getObservationBean().getObservationFormShortcuts(user.getOrganizationId()); + Collections.sort(observations); Collections.reverse(observations); List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList(); + request.setAttribute("shortcuts", shortcuts); request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage())); request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request))); request.setAttribute("selectedPestOrganismId", pestOrganismId); @@ -150,15 +154,40 @@ public class ObservationController extends HttpServlet { { try { + Observation observation = new Observation(); + // See if there are presets on the URL string + if(request.getParameter("cropOrganismId") != null) + { + observation.setCropOrganism(em.find(Organism.class, Integer.valueOf(request.getParameter("cropOrganismId")))); + } + + List<Organism> allCrops; + if(request.getParameter("organismId") != null) + { + Integer organismId = Integer.valueOf(request.getParameter("organismId")); + observation.setOrganism(em.find(Organism.class, organismId)); + // If pest has been selected, include only associated crops + allCrops = SessionControllerGetter.getOrganismBean().getPestCrops(organismId); + } + else + { + allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList(); + } + request.setAttribute("observation", observation); + request.setAttribute("shortcut", request.getParameter("observationFormShortcutId") != null ? + em.find(ObservationFormShortcut.class, Integer.valueOf(request.getParameter("observationFormShortcutId"))) + : null + ); + request.setAttribute("hideObservationFormMap", request.getParameter("hideObservationFormMap") != null ? request.getParameter("hideObservationFormMap").equals("true") : false); + request.setAttribute("noBroadcast", request.getParameter("noBroadcast") != null); request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user)); request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter()); request.setAttribute("defaultMapZoom", user.getOrganizationId().getDefaultMapZoom()); request.setAttribute("locationPointOfInterests", SessionControllerGetter.getPointOfInterestBean().getRelevantPointOfInterestsForUser(user)); List<Organism> allPests = em.createNamedQuery("Organism.findAllPests").getResultList(); request.setAttribute("allPests", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allPests, SessionLocaleUtil.getCurrentLocale(request).getLanguage())); - List<Organism> allCrops = em.createNamedQuery("Organism.findAllCrops").getResultList(); request.setAttribute("allCrops", SessionControllerGetter.getOrganismBean().sortOrganismsByLocalName(allCrops, SessionLocaleUtil.getCurrentLocale(request).getLanguage())); // Hierarchy categories request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request))); @@ -190,6 +219,7 @@ public class ObservationController extends HttpServlet { Integer observationId = Integer.valueOf(request.getParameter("observationId")); Observation observation = SessionControllerGetter.getObservationBean().getObservation(observationId);//em.find(Observation.class, observationId); request.setAttribute("observation", observation); + request.setAttribute("noBroadcast", request.getParameter("noBroadcast") != null); request.setAttribute("mapLayers", SessionControllerGetter.getUserBean().getMapLayerJSONForUser(user)); //System.out.println(observation.getGeoinfo()); request.setAttribute("defaultMapCenter",user.getOrganizationId().getDefaultMapCenter()); @@ -277,6 +307,7 @@ public class ObservationController extends HttpServlet { observation.setObservationText(formValidation.getFormField("observationText").getWebValue()); observation.setObservationData(formValidation.getFormField("observationData").getWebValue()); observation.setIsQuantified(formValidation.getFormField("isQuantified").getWebValue() != null); + observation.setLocationIsPrivate(formValidation.getFormField("locationIsPrivate").getWebValue() != null); observation.setBroadcastMessage(formValidation.getFormField("broadcastMessage").getWebValue() != null); boolean sendNotification = false; diff --git a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java index 68311535..6ac249a8 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java @@ -42,6 +42,7 @@ import no.nibio.vips.logic.entity.Message; import no.nibio.vips.logic.entity.MessageIllustration; import no.nibio.vips.logic.entity.MessageIllustrationPK; import no.nibio.vips.logic.entity.Observation; +import no.nibio.vips.logic.entity.ObservationFormShortcut; import no.nibio.vips.logic.entity.ObservationIllustration; import no.nibio.vips.logic.entity.ObservationIllustrationPK; import no.nibio.vips.logic.entity.ObservationStatusType; @@ -560,5 +561,11 @@ public class ObservationBean { .getSingleResult(); this.deleteObservation(observation.getObservationId()); } + + public List<ObservationFormShortcut> getObservationFormShortcuts(Organization organization) { + return em.createNamedQuery("ObservationFormShortcut.findByOrganizationId"). + setParameter("organizationId", organization.getOrganizationId()) + .getResultList(); + } } diff --git a/src/main/java/no/nibio/vips/logic/controller/session/OrganismBean.java b/src/main/java/no/nibio/vips/logic/controller/session/OrganismBean.java index 7728d10d..e1a6a639 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/OrganismBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/OrganismBean.java @@ -111,6 +111,15 @@ public class OrganismBean { } return retVal; } + + public List<Organism> getCropChildren(Organism crop) + { + List<Organism> children = em.createNamedQuery("Organism.findByParentOrganismId").setParameter("parentOrganismId", crop.getOrganismId()).getResultList(); + List<Organism> grandChildren = new ArrayList(); + children.stream().forEach(childCrop -> grandChildren.addAll(this.getCropChildren(childCrop))); + children.addAll(grandChildren); + return children; + } public List<ExternalResource> getUnusedExternalResourcesForOrganism(Organism organism) { StringBuilder sql = new StringBuilder() @@ -264,10 +273,9 @@ public class OrganismBean { { List<Organism> cropList = this.getAllCrops(); Map<Integer, Organism> cropMap = new HashMap<>(); - for(Organism crop:cropList) - { + cropList.forEach((crop) -> { cropMap.put(crop.getOrganismId(), crop); - } + }); return cropMap; } @@ -284,6 +292,42 @@ public class OrganismBean { } } + /** + * + * @param pestOrganismId + * @return the crops associated with this pest (in the public.crop_pest database + */ + public List<Organism> getPestCrops(Integer pestOrganismId) + { + /*Integer[] pestOrganismIds = {pestOrganismId}; + List<CropPest> cropPests = em.createNamedQuery("CropPest.findByPestOrganismId") + .setParameter("pestOrganismId", pestOrganismId) + .getResultList(); + */ + List<CropPest> cropPests = em.createNativeQuery("SELECT * FROM public.crop_pest where :pestOrganismId = ANY(pest_organism_ids)", CropPest.class) + .setParameter("pestOrganismId", pestOrganismId) + .getResultList(); + + /*System.out.println("PestId=" + pestOrganismId); + Syem.out.println("cropPests.size()=" + cropPests.size());*/ + + // Using map to avoid potential duplicates + Map<Integer, Organism> pestCrops = new HashMap(); + if(cropPests != null) + { + cropPests.stream().forEach(cropPest->{ + Organism crop = em.find(Organism.class, cropPest.getCropOrganismId()); + pestCrops.put(crop.getOrganismId(), crop); + if(cropPest.getIncludeAllChildCrops()) + { + this.getCropChildren(crop).stream().forEach(cropChild->pestCrops.put(cropChild.getOrganismId(), cropChild)); + } + }); + } + + return new ArrayList<>(pestCrops.values()); + } + /** * Searching recursively upwards in organism tree to find a cropPest. * @param cropOrganismId id of the crop diff --git a/src/main/java/no/nibio/vips/logic/entity/CropPest.java b/src/main/java/no/nibio/vips/logic/entity/CropPest.java index 20bf148e..c8250cdd 100755 --- a/src/main/java/no/nibio/vips/logic/entity/CropPest.java +++ b/src/main/java/no/nibio/vips/logic/entity/CropPest.java @@ -46,6 +46,7 @@ import org.hibernate.annotations.TypeDefs; @NamedQuery(name = "CropPest.findAll", query = "SELECT c FROM CropPest c"), @NamedQuery(name = "CropPest.findByCropOrganismId", query = "SELECT c FROM CropPest c WHERE c.cropOrganismId = :cropOrganismId"), @NamedQuery(name = "CropPest.findByPestOrganismIds", query = "SELECT c FROM CropPest c WHERE c.pestOrganismIds = :pestOrganismIds"), + @NamedQuery(name = "CropPest.findByPestOrganismId", query = "SELECT c FROM CropPest c WHERE :pestOrganismId IN (c.pestOrganismIds)"), @NamedQuery(name = "CropPest.findByIncludeAllChildCrops", query = "SELECT c FROM CropPest c WHERE c.includeAllChildCrops = :includeAllChildCrops")}) public class CropPest implements Serializable { diff --git a/src/main/java/no/nibio/vips/logic/entity/Observation.java b/src/main/java/no/nibio/vips/logic/entity/Observation.java index 85f18cb7..0329a9d5 100755 --- a/src/main/java/no/nibio/vips/logic/entity/Observation.java +++ b/src/main/java/no/nibio/vips/logic/entity/Observation.java @@ -94,6 +94,7 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse private String observationData; private Boolean isQuantified; private Boolean broadcastMessage; + private Boolean locationIsPrivate; private String observationDataSchema; @@ -551,4 +552,19 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse this.location = location; } + /** + * @return the locationIsPrivate + */ + @Column(name = "location_is_private") + public Boolean getLocationIsPrivate() { + return locationIsPrivate; + } + + /** + * @param locationIsPrivate the locationIsPrivate to set + */ + public void setLocationIsPrivate(Boolean locationIsPrivate) { + this.locationIsPrivate = locationIsPrivate; + } + } diff --git a/src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcut.java b/src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcut.java new file mode 100644 index 00000000..350cb424 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcut.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.logic.entity; + +import java.io.Serializable; +import java.util.Set; +import javax.persistence.Basic; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Entity +@Table(name = "observation_form_shortcut") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "ObservationFormShortcut.findAll", query = "SELECT o FROM ObservationFormShortcut o") + , @NamedQuery(name = "ObservationFormShortcut.findByObservationFormShortcutId", query = "SELECT o FROM ObservationFormShortcut o WHERE o.observationFormShortcutId = :observationFormShortcutId") + , @NamedQuery(name = "ObservationFormShortcut.findByOrganizationId", query = "SELECT o FROM ObservationFormShortcut o WHERE o.organizationId = :organizationId") + , @NamedQuery(name = "ObservationFormShortcut.findByDefaultLabel", query = "SELECT o FROM ObservationFormShortcut o WHERE o.defaultLabel = :defaultLabel") +}) +public class ObservationFormShortcut implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic(optional = false) + @Column(name = "observation_form_shortcut_id") + private Integer observationFormShortcutId; + @Column(name = "organization_id") + private Integer organizationId; + @Size(max = 255) + @Column(name = "default_label") + private String defaultLabel; + @Size(max = 2047) + @Column(name = "form_query_param_string") + private String formQueryParamString; + @Size(max = 2047) + @Column(name = "list_query_param_string") + private String listQueryParamString; + @OneToMany(cascade = CascadeType.ALL, mappedBy = "observationFormShortcutId", fetch = FetchType.EAGER) + private Set<ObservationFormShortcutLocale> shortcutLocaleSet; + + public ObservationFormShortcut() { + } + + public ObservationFormShortcut(Integer observationFormShortcutId) { + this.observationFormShortcutId = observationFormShortcutId; + } + + public Integer getObservationFormShortcutId() { + return observationFormShortcutId; + } + + public void setObservationFormShortcutId(Integer observationFormShortcutId) { + this.observationFormShortcutId = observationFormShortcutId; + } + + public String getDefaultLabel() { + return defaultLabel; + } + + public void setDefaultLabel(String defaultLabel) { + this.defaultLabel = defaultLabel; + } + + public String getFormQueryParamString() { + return formQueryParamString; + } + + public void setFormQueryParamString(String formQueryParamString) { + this.formQueryParamString = formQueryParamString; + } + + public String getLocalLabel(String language) + { + if(this.shortcutLocaleSet == null || this.shortcutLocaleSet.isEmpty()) + { + return this.getDefaultLabel(); + } + ObservationFormShortcutLocale l = this.shortcutLocaleSet.stream() + .filter(localS -> localS.getLocale().equals(language)) + .findAny() + .orElse( + this.shortcutLocaleSet.stream() + .filter(localS -> localS.getLocale().equals("en")) + .findAny() + .orElse(null) + ); + return l != null ? l.getLabel() : this.getDefaultLabel(); + } + + @Override + public int hashCode() { + int hash = 0; + hash += (observationFormShortcutId != null ? observationFormShortcutId.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof ObservationFormShortcut)) { + return false; + } + ObservationFormShortcut other = (ObservationFormShortcut) object; + if ((this.observationFormShortcutId == null && other.observationFormShortcutId != null) || (this.observationFormShortcutId != null && !this.observationFormShortcutId.equals(other.observationFormShortcutId))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "no.nibio.vips.logic.entity.ObservationFormShortcut[ observationFormShortcutId=" + observationFormShortcutId + " ]"; + } + + /** + * @return the organizationId + */ + public Integer getOrganizationId() { + return organizationId; + } + + /** + * @param organizationId the organizationId to set + */ + public void setOrganizationId(Integer organizationId) { + this.organizationId = organizationId; + } + + /** + * @return the listQueryParamString + */ + public String getListQueryParamString() { + return listQueryParamString; + } + + /** + * @param listQueryParamString the listQueryParamString to set + */ + public void setListQueryParamString(String listQueryParamString) { + this.listQueryParamString = listQueryParamString; + } + +} diff --git a/src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcutLocale.java b/src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcutLocale.java new file mode 100644 index 00000000..470eaeba --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/entity/ObservationFormShortcutLocale.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.logic.entity; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Entity +@Table(name = "observation_form_shortcut_locale") +@XmlRootElement +@NamedQueries({ + @NamedQuery(name = "ObservationFormShortcutLocale.findAll", query = "SELECT o FROM ObservationFormShortcutLocale o") + , @NamedQuery(name = "ObservationFormShortcutLocale.findByObservationFormShortcutId", query = "SELECT o FROM ObservationFormShortcutLocale o WHERE o.observationFormShortcutId = :observationFormShortcutId") + , @NamedQuery(name = "ObservationFormShortcutLocale.findByLocale", query = "SELECT o FROM ObservationFormShortcutLocale o WHERE o.locale = :locale") + , @NamedQuery(name = "ObservationFormShortcutLocale.findByLabel", query = "SELECT o FROM ObservationFormShortcutLocale o WHERE o.label = :label")}) +public class ObservationFormShortcutLocale implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic(optional = false) + @Column(name = "observation_form_shortcut_id") + private Integer observationFormShortcutId; + @Size(max = 10) + @Column(name = "locale") + private String locale; + @Size(max = 255) + @Column(name = "label") + private String label; + + public ObservationFormShortcutLocale() { + } + + public ObservationFormShortcutLocale(Integer observationFormShortcutId) { + this.observationFormShortcutId = observationFormShortcutId; + } + + public Integer getObservationFormShortcutId() { + return observationFormShortcutId; + } + + public void setObservationFormShortcutId(Integer observationFormShortcutId) { + this.observationFormShortcutId = observationFormShortcutId; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public int hashCode() { + int hash = 0; + hash += (observationFormShortcutId != null ? observationFormShortcutId.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + // TODO: Warning - this method won't work in the case the id fields are not set + if (!(object instanceof ObservationFormShortcutLocale)) { + return false; + } + ObservationFormShortcutLocale other = (ObservationFormShortcutLocale) object; + if ((this.observationFormShortcutId == null && other.observationFormShortcutId != null) || (this.observationFormShortcutId != null && !this.observationFormShortcutId.equals(other.observationFormShortcutId))) { + return false; + } + return true; + } + + @Override + public String toString() { + return "no.nibio.vips.logic.entity.ObservationFormShortcutLocale[ observationFormShortcutId=" + observationFormShortcutId + " ]"; + } + +} diff --git a/src/main/java/no/nibio/vips/logic/service/VIPSMobileService.java b/src/main/java/no/nibio/vips/logic/service/VIPSMobileService.java index afa72b28..2d4e3454 100755 --- a/src/main/java/no/nibio/vips/logic/service/VIPSMobileService.java +++ b/src/main/java/no/nibio/vips/logic/service/VIPSMobileService.java @@ -152,7 +152,9 @@ public class VIPSMobileService { List<Observation> broadcastObservations = SessionControllerGetter.getObservationBean().getBroadcastObservations(organizationId); // Making an observation message valid for 3 months by default Calendar cal = Calendar.getInstance(); - broadcastObservations.stream().forEach( + broadcastObservations.stream().filter( + obs -> obs.getBroadcastMessage() && !obs.getObservationHeading().trim().isEmpty() + ).forEach( obs -> { cal.setTime(obs.getTimeOfObservation()); cal.add(Calendar.MONTH, 3); diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties index afa6c26a..50323a83 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties @@ -431,3 +431,8 @@ cropCategoryUpdated=Crop category was updated observationMap=Observation map isForecastLocation=Is forecast location viewOthersObservations=View observations made by others +registrationOf=Registration of +observationData=Observation data +registrationShortcuts=Registration shortcuts +filterList=Filter list +locationIsPrivate=Location is private diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties index a3c5a0a2..57fa0806 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties @@ -431,3 +431,8 @@ cropCategoryUpdated=Crop category was updated observationMap=Observation map isForecastLocation=Is forecast location viewOthersObservations=View observations made by others +registrationOf=Registration of +observationData=Observation data +registrationShortcuts=Registration shortcuts +filterList=Filter list +locationIsPrivate=Location is private diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties index 6bd7d249..81cabccb 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties @@ -430,3 +430,8 @@ cropCategoryUpdated=Crop category was updated observationMap=Observation map isForecastLocation=Is forecast location viewOthersObservations=View observations made by others +registrationOf=Registration of +observationData=Observation data +registrationShortcuts=Registration shortcuts +filterList=Filter list +locationIsPrivate=Location is private diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties index 39eae359..5347c0f2 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties @@ -431,3 +431,8 @@ cropCategoryUpdated=Kulturkategorien ble oppdatert observationMap=Observasjonskart isForecastLocation=Er en varselslokalitet viewOthersObservations=Se observasjoner gjort av andre +registrationOf=Registrering av +observationData=Observasjonsdata +registrationShortcuts=Snarveier for registrering +filterList=Filtr\u00e9r lista +locationIsPrivate=Lokalitet skal ikke offentliggj\u00f8res diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties index 885269ae..6e581f40 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties @@ -431,3 +431,8 @@ cropCategoryUpdated=Crop category was updated observationMap=Observation map isForecastLocation=Is forecast location viewOthersObservations=View observations made by others +registrationOf=Registration of +observationData=Observation data +registrationShortcuts=Registration shortcuts +filterList=Filter list +locationIsPrivate=Location is private diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties index 01a2f364..bc2ded2d 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties @@ -428,3 +428,8 @@ uppercase=\u5927\u5199 allSystems=\u6240\u6709\u7cfb\u7edf cropCategoriesFor=\u4f5c\u7269\u7c7b\u522b\u4e3a viewOthersObservations=View observations made by others +registrationOf=Registration of +observationData=Observation data +registrationShortcuts=Registration shortcuts +filterList=Filter list +locationIsPrivate=Location is private diff --git a/src/main/webapp/formdefinitions/observationForm.json b/src/main/webapp/formdefinitions/observationForm.json index 03d1f0ea..df120cc8 100755 --- a/src/main/webapp/formdefinitions/observationForm.json +++ b/src/main/webapp/formdefinitions/observationForm.json @@ -57,6 +57,11 @@ "fieldType" : "SELECT_SINGLE", "required" : false }, + { + "name" : "locationIsPrivate", + "dataType" : "STRING", + "required" : false + }, { "name" : "isQuantified", "dataType" : "STRING", diff --git a/src/main/webapp/templates/observationForm.ftl b/src/main/webapp/templates/observationForm.ftl index 0527d29a..f3618a32 100755 --- a/src/main/webapp/templates/observationForm.ftl +++ b/src/main/webapp/templates/observationForm.ftl @@ -1,5 +1,5 @@ <#-- - Copyright (c) 2014 NIBIO <http://www.nibio.no/>. + Copyright (c) 2017 NIBIO <http://www.nibio.no/>. This file is part of VIPSLogic. VIPSLogic is free software: you can redistribute it and/or modify @@ -69,9 +69,9 @@ <#if observation.observationData?has_content> observationData = ${observation.observationData}; getDataSchema(${observation.organism.organismId}, organizationId); - <#else> + <#elseif observation.organism?has_content> // Setting - + initObservationData(${observation.organism.organismId},organizationId); </#if> // Activating file selection @@ -245,11 +245,19 @@ } - // If locationPointOfInterestId is selected, show map for locations + // If locationPointOfInterestId is selected OR observation drawing map is deliberately disabled, + // show map for locations // If not, show the observation drawing map + var hideObservationFormMap = ${hideObservationFormMap?c!"false"}; function showCorrectMap(){ var locationList = document.getElementById("locationPointOfInterestId"); - if(locationList.options[locationList.options.selectedIndex].value != "-1") + if(hideObservationFormMap && locationList.options[locationList.options.selectedIndex].value == "-1") + { + document.getElementById("observationFormMap").style.display="none"; + document.getElementById("poiFormMap").style.display="block"; + initLocationMap(null); + } + else if(locationList.options[locationList.options.selectedIndex].value != "-1") { document.getElementById("observationFormMap").style.display="none"; document.getElementById("poiFormMap").style.display="block"; @@ -374,12 +382,24 @@ cropOrganismIdList.options[cropOrganismIdList.options.length] = theRest[i]; } } + <#if noBroadcast> + /** + * In the unlikely event that the user in a pre-filled form decides to + * change the organism, we'll update the observationText and heading + */ + function updateHeadingAndText(organismName) + { + var registrationOfText = "${i18nBundle.registrationOf} " + organismName; + document.getElementById("observationHeading").value = registrationOfText; + document.getElementById("observationText").value = registrationOfText; + } + </#if> </script> </#macro> <#macro page_contents> <div class="singleBlockContainer"> <p><a href="/observation" class="btn btn-default back" role="button">${i18nBundle.back}</a><#if observation.observationId?has_content><a href="/observation?action=newObservationForm" class="btn btn-default" role="button">${i18nBundle.addNew}</a></#if></p> - <h1><#if observation.observationId?has_content>${i18nBundle.editObservation}<#else>${i18nBundle.newObservation}</#if></h1> + <h1><#if observation.observationId?has_content>${i18nBundle.editObservation}<#else>${i18nBundle.newObservation}</#if><#if shortcut?has_content> - ${shortcut.getLocalLabel(currentLocale.language)?lower_case}</#if></h1> <div id="errorMsgEl" class="alert alert-danger" <#if !formValidation?has_content> style="display:none;"</#if>> <#if formValidation?has_content>${formValidation.validationMessages?replace("\n", "<br>")}</#if> </div> @@ -389,7 +409,7 @@ <div class="row"> <div class="col-md-6"> <#assign formId = "observationForm"> - <form id="${formId}" role="form" action="/observation?action=observationFormSubmit" enctype="multipart/form-data" method="POST" onsubmit="this['geoInfo'].value=getFeatures();try{mw.save();this['observationData'].value=JSON.stringify(mw.toInspect);return validateForm(this) && validateGIS(this);}catch(e){console.log(e.message);console.log(e);return false;}"> + <form id="${formId}" role="form" action="/observation?action=observationFormSubmit" enctype="multipart/form-data" method="POST" onsubmit="this['geoInfo'].value=getFeatures();try{mw.save();this['observationData'].value=JSON.stringify(mw.toInspect);console.info('validateGIS = ' + (validateGIS(this)));return validateForm(this) && validateGIS(this);}catch(e){console.log(e.message);console.log(e);return false;}"> <!--form id="${formId}" role="form" action="/observation?action=observationFormSubmit" method="POST" onsubmit="this['geoInfo'].value=getFeatures();mw.save();console.log(this['observationData']);this['observationData'].value=JSON.stringify(mw.toInspect);return validateForm(this);"--> <!--form id="${formId}" role="form" action="/observation?action=observationFormSubmit" method="POST" onsubmit="this['geoInfo'].value=getFeatures();mw.save();console.log(this['observationData']);this['observationData'].value=JSON.stringify(mw.toInspect);validateForm(this);return false;"--> <input type="hidden" name="geoInfo" value=""/> @@ -407,17 +427,17 @@ </#if> <div class="form-group"> <label for="cropOrganismId">${i18nBundle.cropOrganismId}</label> - <select class="form-control" id="cropOrganismIdList" name="cropOrganismId" <#if observation.organism?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly" <#else> onblur="validateField(this);" onchange="updateCropPests();"</#if>> - <#if ! observation.organism?has_content || user.isSuperUser() || user.isOrganizationAdmin()> + <select class="form-control" id="cropOrganismIdList" name="cropOrganismId" <#if observation.observationId?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly" <#else> onblur="validateField(this);" onchange="updateCropPests();"</#if>> + <#if ! observation.observationId?has_content || user.isSuperUser() || user.isOrganizationAdmin()> <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.cropOrganismId?lower_case}</option> <option value="-10"<#if (observation.cropOrganism?has_content && observation.cropOrganism.organismId == -10)>selected="selected"</#if>>${i18nBundle.missingInDatabase}</option> - <#list allCrops as cropOrganism> + <#list allCrops as cropOrganism> <#if cropOrganism.hierarchyCategoryId <= 121> <option value="${cropOrganism.organismId}" <#if (observation.cropOrganism?has_content && observation.cropOrganism.organismId == cropOrganism.organismId)>selected="selected"</#if> >${cropOrganism.getLocalName(currentLocale.language)!""} (${cropOrganism.latinName!""}) ${hierarchyCategories.getName(cropOrganism.hierarchyCategoryId)?upper_case}</option> </#if> - </#list> + </#list> <#else> <#list allCrops as cropOrganism> <#if (observation.cropOrganism?has_content && observation.cropOrganism.organismId == cropOrganism.organismId)> @@ -430,7 +450,7 @@ </div> <div class="form-group"> <label for="organismId">${i18nBundle.organism}</label> - <select class="form-control" name="organismId" <#if observation.organism?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly"<#else>onchange="initObservationData(this.options[this.options.selectedIndex].value,organizationId);" onblur="validateField(this);"</#if>> + <select class="form-control" name="organismId" <#if observation.organism?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly"<#else>onchange="<#if noBroadcast>updateHeadingAndText(this.options[this.options.selectedIndex].text);</#if>initObservationData(this.options[this.options.selectedIndex].value,organizationId);" onblur="validateField(this);"</#if>> <#if ! observation.organism?has_content || user.isSuperUser() || user.isOrganizationAdmin()> <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.organism?lower_case}</option> <option value="-10"<#if (observation.organism?has_content && observation.organism.organismId == -10)>selected="selected"</#if>>${i18nBundle.missingInDatabase}</option> @@ -451,12 +471,20 @@ </div> <div class="form-group"> - <label for="locationPointOfInterestId">${i18nBundle.location} <i class="fa fa-plus-square" aria-hidden="true" style="cursor:pointer;" title="${i18nBundle.addNew}" onclick="addNewLocationPopup();"></i></label> + <label for="locationPointOfInterestId">${i18nBundle.location} <button role="button" type="button" onclick="addNewLocationPopup();">${i18nBundle.addNew}</button></label> <select class="form-control" name="locationPointOfInterestId" id="locationPointOfInterestId" onchange="showCorrectMap();"> <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.location?lower_case}</option> </select> <span class="help-block" id="${formId}_locationPointOfInterestId_validation"></span> </div> + <div class="form-group"> + <div class="checkbox"> + <label> + <input type="checkbox" name="locationIsPrivate"<#if !observation.locationIsPrivate?has_content || observation.locationIsPrivate == false><#else>checked="checked"</#if>/> + </label> + ${i18nBundle.locationIsPrivate} + </div> + </div> <#setting time_zone=user.organizationId.defaultTimeZone!"UTC"> <div class="form-group"> <label for="timeOfObservation">${i18nBundle.timeOfObservation}</label> @@ -474,67 +502,52 @@ </div> <div class="form-group"> <fieldset> - <legend>Observation data</legend> + <legend>${i18nBundle.observationData}</legend> <div id="metawidget"></div> </fieldset> </div> - <!--div class="form-group"> - <label for="observedValue">${i18nBundle.observedValue}</label> - <input type="number" class="form-control" name="observedValue" placeholder="${i18nBundle.observedValue}" value="${(observation.observedValue?c)!""}" onblur="validateField(this);"/> - <span class="help-block" id="${formId}_observedValue_validation"></span> - </div> - <div class="form-group"> - <label for="denominator">${i18nBundle.denominator}</label> - <input type="number" class="form-control" name="denominator" placeholder="${i18nBundle.denominator}" value="${(observation.denominator?c)!"1"}" onblur="validateField(this);"/> - <span class="help-block" id="${formId}_denominator_validation"></span> - </div--> - <!--div class="form-group"> - <label for="observationMethodId">${i18nBundle.observationMethodId}</label> - <select class="form-control" name="observationMethodId" onblur="validateField(this);"> - <#list observationMethods as observationMethod> - <option value="${observationMethod.observationMethodId}" - <#if observation.observationMethodId?has_content && observation.observationMethodId.observationMethodId == observationMethod.observationMethodId>selected="selected"</#if> - >${i18nBundle("observationMethodTitle_" + observationMethod.observationMethodId)}</option> - </#list> - </select> - <span class="help-block" id="${formId}_observationMethodId_validation"></span> - </div--> + <#if !noBroadcast> <div class="form-group"> <div class="checkbox"> <label> - <input type="checkbox" name="broadcastMessage"<#if observation.broadcastMessage?has_content && observation.broadcastMessage == false><#else>checked="checked"</#if>/> + <input type="checkbox" name="broadcastMessage"<#if (observation.broadcastMessage?has_content && observation.broadcastMessage == false) || noBroadcast><#else>checked="checked"</#if>/> </label> ${i18nBundle.broadcastMessage} </div> </div> - <div class="form-group"> - <label for="observationHeading">${i18nBundle.observationHeading}</label> - <input type="text" class="form-control" name="observationHeading" placeholder="" value="${observation.observationHeading!""}" onblur="validateField(this);"/> - <span class="help-block" id="${formId}_observationHeading_validation"></span> - </div> - <div class="form-group"> - <label for="observationText">${i18nBundle.observationText}</label> - <textarea class="form-control" name="observationText" placeholder="" >${observation.observationText!""}</textarea> - <span class="help-block" id="${formId}_observationText_validation"></span> - </div> - <#if observation.observationIllustrationSet?has_content && observation.observationIllustrationSet?size == 1> - <#assign illustration = observation.observationIllustrationSet?first> - <img src="/static/images/observations/${observation.organismId}/${illustration.observationIllustrationPK.fileName}" alt="TODO: Add describing text" class="img-responsive"/> - <div class="checkbox"> - <label> - <input type="checkbox" name="deleteIllustration" value="true"> - ${i18nBundle.deleteIllustration} - </label> - </div> - </#if> - <div class="form-group"> - <div class="input-group"> - <label for="illustration"><#if observation.observationIllustrationSet?has_content && observation.observationIllustrationSet?size == 1>${i18nBundle.replaceIllustration}<#else>${i18nBundle.newIllustration}</#if></label><br/> - <span class="btn btn-default btn-file">${i18nBundle.browse}<input type="file" name="illustration"></span> - <input type="text" class="form-control" readonly> - </div> - </div> - + + <div class="form-group"> + <label for="observationHeading">${i18nBundle.observationHeading}</label> + <input type="text" class="form-control" name="observationHeading" placeholder="" value="${observation.observationHeading!""}" onblur="validateField(this);"/> + <span class="help-block" id="${formId}_observationHeading_validation"></span> + </div> + <div class="form-group"> + <label for="observationText">${i18nBundle.observationText}</label> + <textarea class="form-control" name="observationText" placeholder="" >${observation.observationText!""}</textarea> + <span class="help-block" id="${formId}_observationText_validation"></span> + </div> + <#if observation.observationIllustrationSet?has_content && observation.observationIllustrationSet?size == 1> + <#assign illustration = observation.observationIllustrationSet?first> + <img src="/static/images/observations/${observation.organismId}/${illustration.observationIllustrationPK.fileName}" alt="TODO: Add describing text" class="img-responsive"/> + <div class="checkbox"> + <label> + <input type="checkbox" name="deleteIllustration" value="true"> + ${i18nBundle.deleteIllustration} + </label> + </div> + </#if> + <div class="form-group"> + <div class="input-group"> + <label for="illustration"><#if observation.observationIllustrationSet?has_content && observation.observationIllustrationSet?size == 1>${i18nBundle.replaceIllustration}<#else>${i18nBundle.newIllustration}</#if></label><br/> + <span class="btn btn-default btn-file">${i18nBundle.browse}<input type="file" name="illustration"></span> + <input type="text" class="form-control" readonly> + </div> + </div> + <#else> + <!-- broadcastMessage is checkbox, omitting it sets it default to false --> + <input type="hidden" id="observationHeading" name="observationHeading" value="<#if observation.organism?has_content>${i18nBundle.registrationOf} ${observation.organism.getLocalName(currentLocale.language)!""} (${observation.organism.latinName!""})</#if>"/> + <input type="hidden" id="observationText" name="observationText" value="<#if observation.organism?has_content>${i18nBundle.registrationOf} ${observation.organism.getLocalName(currentLocale.language)!""} (${observation.organism.latinName!""})</#if>"/> + </#if> <!-- visibility --> <#if user.isObservationAuthority() || user.isOrganizationAdmin() || user.isSuperUser()> <div class="form-group"> <label for="statusTypeId">${i18nBundle.statusTypeId}</label> diff --git a/src/main/webapp/templates/observationList.ftl b/src/main/webapp/templates/observationList.ftl index ff23a9ed..28ef1097 100755 --- a/src/main/webapp/templates/observationList.ftl +++ b/src/main/webapp/templates/observationList.ftl @@ -27,6 +27,14 @@ <div class="alert alert-success">${i18nBundle(messageKey)}</div> </#if> <a href="/observation?action=newObservationForm" class="btn btn-default" role="button">${i18nBundle.addNew}</a> + <#if shortcuts?has_content> + <h4>${i18nBundle.registrationShortcuts}</h4> + <#list shortcuts as shortcut> + <a href="/observation?action=newObservationForm${shortcut.formQueryParamString}&observationFormShortcutId=${shortcut.observationFormShortcutId}" class="btn btn-default" role="button">${shortcut.getLocalLabel(currentLocale.language)}</a> + </#list> + <h4>${i18nBundle.filterList}</h4> + </#if> + <form action="/observation" method="post"> <select class="form-control" name="statusTypeId" onchange="this.form.submit();"> <#list [-1,1,2,3] as statusTypeId> @@ -55,6 +63,11 @@ </div> </#if> </form> + <#if shortcuts?has_content> + <#list shortcuts as shortcut> + <a href="/observation?${shortcut.listQueryParamString}" class="btn btn-default" role="button">${shortcut.getLocalLabel(currentLocale.language)}</a> + </#list> + </#if> <#if observations?size == 0> <div class="alert alert-warning">${i18nBundle.noResultsFoundForCriteria}</div> </#if> -- GitLab