From 83dd88403fe775c72020f1f16dacef1670cff64d Mon Sep 17 00:00:00 2001 From: Hector Plahar Date: Fri, 22 Jan 2021 13:03:59 -0800 Subject: [PATCH 1/4] v5.9.0 label --- pom.xml | 2 +- src/main/java/org/jbei/ice/lib/config/SiteSettings.java | 2 +- src/main/webapp/views/footer.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 3a182199f..e9c3a063e 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.jbei ice war - 5.8.3 + 5.9.0 ice Inventory of Composable Elements (ICE) for Synthetic Biology diff --git a/src/main/java/org/jbei/ice/lib/config/SiteSettings.java b/src/main/java/org/jbei/ice/lib/config/SiteSettings.java index f3d62ec4d..e0e6cebf3 100644 --- a/src/main/java/org/jbei/ice/lib/config/SiteSettings.java +++ b/src/main/java/org/jbei/ice/lib/config/SiteSettings.java @@ -9,7 +9,7 @@ */ public class SiteSettings implements IDataTransferModel { - private String version = "5.8.3"; + private String version = "5.9.0"; private String assetName; private boolean hasLogo; private boolean hasLoginMessage; diff --git a/src/main/webapp/views/footer.html b/src/main/webapp/views/footer.html index 1c1d19bc2..c6083b5e3 100644 --- a/src/main/webapp/views/footer.html +++ b/src/main/webapp/views/footer.html @@ -11,7 +11,7 @@
© JBEI ICE Registry 5.8.3
+ class="label label-primary">5.9.0
All rights reserved.
Submit an Issue  |  Help From fe3e80b49a0793fb7e06fc36d98aa37ba53d3b59 Mon Sep 17 00:00:00 2001 From: Hector Plahar Date: Fri, 22 Jan 2021 13:27:51 -0800 Subject: [PATCH 2/4] package upgrades --- pom.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index e9c3a063e..dc2380019 100755 --- a/pom.xml +++ b/pom.xml @@ -18,33 +18,33 @@ org.glassfish.jersey.containers jersey-container-servlet-core - 2.30.1 + 2.32 org.glassfish.jersey.media jersey-media-multipart - 2.30.1 + 2.32 org.glassfish.jersey.inject jersey-hk2 - 2.30.1 + 2.32 javax.servlet javax.servlet-api - 3.1.0 + 4.0.1 provided org.postgresql postgresql - 42.2.6 + 42.2.16 com.h2database h2 - 1.4.199 + 1.4.200 junit @@ -55,18 +55,18 @@ com.google.code.gson gson - 2.8.5 + 2.8.6 compile org.hibernate hibernate-core - 5.4.14.Final + 5.4.22.Final org.hibernate hibernate-search-orm - 5.11.5.Final + 5.11.8.Final javax.validation @@ -86,7 +86,7 @@ org.hibernate hibernate-c3p0 - 5.4.14.Final + 5.4.22.Final commons-io @@ -101,7 +101,7 @@ com.opencsv opencsv - 5.1 + 5.2 ch.qos.logback @@ -150,7 +150,7 @@ io.undertow undertow-servlet - 1.4.18.Final + 2.2.0.Final From 3cf4d72da157ff033650e6e402bb998e677b29f7 Mon Sep 17 00:00:00 2001 From: Hector Plahar Date: Tue, 26 Jan 2021 13:19:34 -0800 Subject: [PATCH 3/4] adding content types to folder info return --- .../ice/lib/dto/folder/FolderDetails.java | 11 ++++++++++ .../org/jbei/ice/lib/folder/UserFolders.java | 9 ++++++++ .../ice/storage/hibernate/dao/FolderDAO.java | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/main/java/org/jbei/ice/lib/dto/folder/FolderDetails.java b/src/main/java/org/jbei/ice/lib/dto/folder/FolderDetails.java index 158f48a72..f28050ab6 100755 --- a/src/main/java/org/jbei/ice/lib/dto/folder/FolderDetails.java +++ b/src/main/java/org/jbei/ice/lib/dto/folder/FolderDetails.java @@ -1,10 +1,14 @@ package org.jbei.ice.lib.dto.folder; import org.jbei.ice.lib.account.AccountTransfer; +import org.jbei.ice.lib.dto.entry.EntryType; import org.jbei.ice.lib.dto.sample.SampleRequest; import org.jbei.ice.lib.dto.web.RegistryPartner; import org.jbei.ice.lib.folder.AbstractFolder; +import java.util.ArrayList; +import java.util.List; + /** * Folder Transfer Object * @@ -23,14 +27,17 @@ public class FolderDetails extends AbstractFolder { private FolderDetails parent; private RegistryPartner remotePartner; private SampleRequest sampleRequest; + private final List contentTypes; // types of entries contained in this folder public FolderDetails() { super(); + this.contentTypes = new ArrayList<>(); } public FolderDetails(long id, String name) { super(id); this.folderName = name; + this.contentTypes = new ArrayList<>(); } public String getName() { @@ -123,4 +130,8 @@ public SampleRequest getSampleRequest() { public void setSampleRequest(SampleRequest sampleRequest) { this.sampleRequest = sampleRequest; } + + public List getContentTypes() { + return this.contentTypes; + } } diff --git a/src/main/java/org/jbei/ice/lib/folder/UserFolders.java b/src/main/java/org/jbei/ice/lib/folder/UserFolders.java index ba31d9ab4..50fea7d3d 100644 --- a/src/main/java/org/jbei/ice/lib/folder/UserFolders.java +++ b/src/main/java/org/jbei/ice/lib/folder/UserFolders.java @@ -1,5 +1,6 @@ package org.jbei.ice.lib.folder; +import org.jbei.ice.lib.dto.entry.EntryType; import org.jbei.ice.lib.dto.folder.FolderAuthorization; import org.jbei.ice.lib.dto.folder.FolderDetails; import org.jbei.ice.lib.dto.folder.FolderType; @@ -43,6 +44,14 @@ public FolderDetails get(long folderId) { authorization.expectRead(this.userId, folder); FolderDetails folderDetails = folder.toDataTransferObject(); + List types = dao.getContentTypes(folderId); + if (!types.isEmpty()) { + for (String type : types) { + EntryType entryType = EntryType.nameToType(type); + if (entryType != null) + folderDetails.getContentTypes().add(entryType); + } + } Account owner = accountDAO.getByEmail(folder.getOwnerEmail()); if (owner != null) { diff --git a/src/main/java/org/jbei/ice/storage/hibernate/dao/FolderDAO.java b/src/main/java/org/jbei/ice/storage/hibernate/dao/FolderDAO.java index a5f74d732..ddac8f7bd 100755 --- a/src/main/java/org/jbei/ice/storage/hibernate/dao/FolderDAO.java +++ b/src/main/java/org/jbei/ice/storage/hibernate/dao/FolderDAO.java @@ -388,4 +388,25 @@ public long getEntryCountByFolderType(FolderType type, String filter) { throw new DAOException(e); } } + + /** + * Retrieves distinct list of types for the entries contained in the folder + * + * @param folderId unique identifier for folder whose content types are being retrieved + * @return list of content types. e.g. if a folder contains 345 Strains, and 23 plasmids, this will return + * [STRAIN, PLASMID]. Any empty folder will result in a return of an empty list + */ + public List getContentTypes(long folderId) { + try { + CriteriaQuery query = getBuilder().createQuery(String.class); + Root from = query.from(Folder.class); + Join entry = from.join("contents"); + query.select(entry.get("recordType")).distinct(true); + query.where(getBuilder().equal(from.get("id"), folderId)); + return currentSession().createQuery(query).list(); + } catch (HibernateException e) { + Logger.error(e); + throw new DAOException(e); + } + } } From e5169324c99158afe8562168c9f21c27ee124d5c Mon Sep 17 00:00:00 2001 From: Hector Plahar Date: Wed, 27 Jan 2021 12:43:51 -0800 Subject: [PATCH 4/4] bug fix: selecting "all" of a specific type was not returning those types. E.g. when a user selected "all plasmids" and exported, the export was empty --- src/main/webapp/scripts/controllers.js | 4 +- src/main/webapp/scripts/entry/entryService.js | 135 +++++++++--------- .../scripts/entry/export/exportController.js | 72 +++++----- 3 files changed, 110 insertions(+), 101 deletions(-) diff --git a/src/main/webapp/scripts/controllers.js b/src/main/webapp/scripts/controllers.js index 03246c844..16c8b7ef3 100755 --- a/src/main/webapp/scripts/controllers.js +++ b/src/main/webapp/scripts/controllers.js @@ -31,7 +31,7 @@ iceControllers.controller('ActionMenuController', function ($stateParams, $uibMo } var i = $scope.selectedFolders.indexOf(folder); - if (i == -1) { + if (i === -1) { $scope.selectedFolders.push(folder); } else { $scope.selectedFolders.splice(i, 1); @@ -61,7 +61,7 @@ iceControllers.controller('ActionMenuController', function ($stateParams, $uibMo } var entrySelection = { - all: Selection.getSelection().type == 'ALL', + all: Selection.getSelection().type === 'ALL', folderId: folderSelected, selectionType: selectionType, entryType: Selection.getSelection().type, diff --git a/src/main/webapp/scripts/entry/entryService.js b/src/main/webapp/scripts/entry/entryService.js index e24ea1593..6f9b02f91 100644 --- a/src/main/webapp/scripts/entry/entryService.js +++ b/src/main/webapp/scripts/entry/entryService.js @@ -67,8 +67,9 @@ angular.module('ice.entry.service', []) }, setTypeSelection: function (type) { + this.reset(); + if (!type) { - this.reset(); return; } @@ -88,7 +89,7 @@ angular.module('ice.entry.service', []) let selected = []; for (let k in selectedEntries) { if (selectedEntries.hasOwnProperty(k) && selectedEntries[k]) { - selected.push({ id: k, visible: selectedEntries[k].visible }); + selected.push({id: k, visible: selectedEntries[k].visible}); } } return selected; @@ -120,7 +121,7 @@ angular.module('ice.entry.service', []) canRestore: function () { if (allSelection.type && allSelection.type === 'ALL') return false; - return this.hasSelection() && this.getSelectedEntries()[0].visible === 'DELETED'; + return this.hasSelection() && this.getSelectedEntries().length && this.getSelectedEntries()[0].visible === 'DELETED'; }, isAdmin: function () { @@ -183,8 +184,8 @@ angular.module('ice.entry.service', []) // inputType of "withEmail" uses attribute "bothRequired" to indicate that the email portion is required // const partFields = [ - { label: "Name", required: true, schema: 'name', inputType: 'short' }, - { label: "Alias", schema: 'alias', inputType: 'short' }, + {label: "Name", required: true, schema: 'name', inputType: 'short'}, + {label: "Alias", schema: 'alias', inputType: 'short'}, { label: "Principal Investigator", required: true, @@ -192,34 +193,34 @@ angular.module('ice.entry.service', []) inputType: 'withEmail', bothRequired: false }, - { label: "Funding Source", schema: 'fundingSource', inputType: 'medium' }, + {label: "Funding Source", schema: 'fundingSource', inputType: 'medium'}, { label: "Status", schema: 'status', required: true, options: [ - { value: "Complete", text: "Complete" }, - { value: "In Progress", text: "In Progress" }, - { value: "Abandoned", text: "Abandoned" }, - { value: "Planned", text: "Planned" } + {value: "Complete", text: "Complete"}, + {value: "In Progress", text: "In Progress"}, + {value: "Abandoned", text: "Abandoned"}, + {value: "Planned", text: "Planned"} ] }, { label: "BioSafety Level", schema: 'bioSafetyLevel', required: true, options: [ - { value: "1", text: "Level 1" }, - { value: "2", text: "Level 2" }, - { value: "-1", text: "Restricted" } + {value: "1", text: "Level 1"}, + {value: "2", text: "Level 2"}, + {value: "-1", text: "Restricted"} ] }, - { label: "Creator", required: true, schema: 'creator', inputType: 'withEmail', bothRequired: true }, - { label: "Keywords", schema: 'keywords', inputType: 'medium' }, - { label: "External URL", schema: 'links', inputType: 'add' }, - { label: "Summary", required: true, schema: 'shortDescription', inputType: 'long' }, - { label: "References", schema: 'references', inputType: 'long' }, - { label: "Intellectual Property", schema: 'intellectualProperty', inputType: 'long' } + {label: "Creator", required: true, schema: 'creator', inputType: 'withEmail', bothRequired: true}, + {label: "Keywords", schema: 'keywords', inputType: 'medium'}, + {label: "External URL", schema: 'links', inputType: 'add'}, + {label: "Summary", required: true, schema: 'shortDescription', inputType: 'long'}, + {label: "References", schema: 'references', inputType: 'long'}, + {label: "Intellectual Property", schema: 'intellectualProperty', inputType: 'long'} ]; // fields peculiar to plasmids const plasmidFields = [ - { label: "Backbone", schema: 'backbone', subSchema: 'plasmidData', inputType: 'medium' }, - { label: "Circular", schema: 'circular', inputType: 'bool', subSchema: 'plasmidData' }, + {label: "Backbone", schema: 'backbone', subSchema: 'plasmidData', inputType: 'medium'}, + {label: "Circular", schema: 'circular', inputType: 'bool', subSchema: 'plasmidData'}, { label: "Origin of Replication", schema: 'originOfReplication', inputType: 'autoComplete', autoCompleteField: 'ORIGIN_OF_REPLICATION', subSchema: 'plasmidData' @@ -255,34 +256,34 @@ angular.module('ice.entry.service', []) }, { label: "Plant Type", schema: 'plantType', subSchema: 'seedData', options: [ - { value: "EMS", text: "EMS" }, - { value: "OVER_EXPRESSION", text: "OVER_EXPRESSION" }, - { value: "RNAI", text: "RNAi" }, - { value: "REPORTER", text: "Reporter" }, - { value: "T_DNA", text: "T-DNA" }, - { value: "OTHER", text: "Other" } + {value: "EMS", text: "EMS"}, + {value: "OVER_EXPRESSION", text: "OVER_EXPRESSION"}, + {value: "RNAI", text: "RNAi"}, + {value: "REPORTER", text: "Reporter"}, + {value: "T_DNA", text: "T-DNA"}, + {value: "OTHER", text: "Other"} ] }, { label: "Generation", schema: 'generation', subSchema: 'seedData', options: [ - { value: "UNKNOWN", text: "UNKNOWN" }, - { value: "F1", text: "F1" }, - { value: "F2", text: "F2" }, - { value: "F3", text: "F3" }, - { value: "M0", text: "M0" }, - { value: "M1", text: "M1" }, - { value: "M2", text: "M2" }, - { value: "T0", text: "T0" }, - { value: "T1", text: "T1" }, - { value: "T2", text: "T2" }, - { value: "T3", text: "T3" }, - { value: "T4", text: "T4" }, - { value: "T5", text: "T5" } + {value: "UNKNOWN", text: "UNKNOWN"}, + {value: "F1", text: "F1"}, + {value: "F2", text: "F2"}, + {value: "F3", text: "F3"}, + {value: "M0", text: "M0"}, + {value: "M1", text: "M1"}, + {value: "M2", text: "M2"}, + {value: "T0", text: "T0"}, + {value: "T1", text: "T1"}, + {value: "T2", text: "T2"}, + {value: "T3", text: "T3"}, + {value: "T4", text: "T4"}, + {value: "T5", text: "T5"} ] }, - { label: "Harvest Date", schema: 'harvestDate', subSchema: 'seedData', inputType: 'date' }, - { label: "Homozygosity", schema: 'homozygosity', subSchema: 'seedData', inputType: 'medium' }, - { label: "Ecotype", schema: 'ecotype', subSchema: 'seedData', inputType: 'medium' }, + {label: "Harvest Date", schema: 'harvestDate', subSchema: 'seedData', inputType: 'date'}, + {label: "Homozygosity", schema: 'homozygosity', subSchema: 'seedData', inputType: 'medium'}, + {label: "Ecotype", schema: 'ecotype', subSchema: 'seedData', inputType: 'medium'}, { label: "Selection Markers", required: true, schema: 'selectionMarkers', inputType: 'autoCompleteAdd', autoCompleteField: 'SELECTION_MARKERS' @@ -295,48 +296,48 @@ angular.module('ice.entry.service', []) label: "Selection Markers", required: true, schema: 'selectionMarkers', inputType: 'autoCompleteAdd', autoCompleteField: 'SELECTION_MARKERS' }, - { label: "Genotype/Phenotype", schema: 'genotypePhenotype', inputType: 'long', subSchema: 'strainData' }, - { label: "Host", schema: 'host', inputType: 'short', subSchema: 'strainData' } + {label: "Genotype/Phenotype", schema: 'genotypePhenotype', inputType: 'long', subSchema: 'strainData'}, + {label: "Host", schema: 'host', inputType: 'short', subSchema: 'strainData'} ]; // fields peculiar to proteins const proteinFields = [ - { label: "Organism", schema: 'organism', inputType: 'medium', subSchema: 'proteinData' }, - { label: "Full Name", schema: 'fullName', inputType: 'medium', subSchema: 'proteinData' }, - { label: "Gene Name", schema: 'geneName', inputType: 'medium', subSchema: 'proteinData' }, - { label: "Uploaded From", schema: 'uploadedFrom', inputType: 'medium', subSchema: 'proteinData' }, + {label: "Organism", schema: 'organism', inputType: 'medium', subSchema: 'proteinData'}, + {label: "Full Name", schema: 'fullName', inputType: 'medium', subSchema: 'proteinData'}, + {label: "Gene Name", schema: 'geneName', inputType: 'medium', subSchema: 'proteinData'}, + {label: "Uploaded From", schema: 'uploadedFrom', inputType: 'medium', subSchema: 'proteinData'}, ]; const generateLinkOptions = function (type) { switch (type.toLowerCase()) { case 'plasmid': return [ - { type: 'part', display: 'Part' }, - { type: 'plasmid', display: 'Plasmid' } + {type: 'part', display: 'Part'}, + {type: 'plasmid', display: 'Plasmid'} ]; case 'part': return [ - { type: 'part', display: 'Part' } + {type: 'part', display: 'Part'} ]; case 'strain': return [ - { type: 'part', display: 'Part' }, - { type: 'plasmid', display: 'Plasmid' }, - { type: 'strain', display: 'Strain' } + {type: 'part', display: 'Part'}, + {type: 'plasmid', display: 'Plasmid'}, + {type: 'strain', display: 'Strain'} ]; case 'arabidopsis': return [ - { type: 'part', display: 'Part' }, - { type: 'arabidopsis', display: 'Seed' } + {type: 'part', display: 'Part'}, + {type: 'arabidopsis', display: 'Seed'} ]; case 'protein': return [ - { type: 'part', display: 'Part' }, - { type: 'protein', display: 'Protein' } + {type: 'part', display: 'Part'}, + {type: 'protein', display: 'Protein'} ]; } }; @@ -492,11 +493,11 @@ angular.module('ice.entry.service', []) customField.options = []; customField.inputType = "options"; custom.options.forEach(function (each) { - customField.options.push({ value: each.name, text: each.name }) + customField.options.push({value: each.name, text: each.name}) }); if (custom.fieldType === "MULTI_CHOICE_PLUS") - customField.options.push({ value: "Other", text: "Other" }); + customField.options.push({value: "Other", text: "Other"}); fields.push(customField); break; @@ -619,7 +620,7 @@ angular.module('ice.entry.service', []) fields.forEach(function (field) { if (field.inputType === 'autoCompleteAdd' || field.inputType === 'add') { entry[field.schema] = [ - { value: '' } + {value: ''} ]; return; } @@ -709,11 +710,11 @@ angular.module('ice.entry.service', []) getEntryItems: function () { return [ - { name: "Plasmid", type: "plasmid" }, - { name: "Strain", type: "strain" }, - { name: "Part", type: "part" }, - { name: "Seed", type: "seed" }, - { name: "Protein", type: "protein" } + {name: "Plasmid", type: "plasmid"}, + {name: "Strain", type: "strain"}, + {name: "Part", type: "part"}, + {name: "Seed", type: "seed"}, + {name: "Protein", type: "protein"} ] } } diff --git a/src/main/webapp/scripts/entry/export/exportController.js b/src/main/webapp/scripts/entry/export/exportController.js index 1a7efe92d..cc630113d 100644 --- a/src/main/webapp/scripts/entry/export/exportController.js +++ b/src/main/webapp/scripts/entry/export/exportController.js @@ -1,33 +1,41 @@ 'use strict'; angular.module('ice.entry.export.controller', []) - .controller('CustomExportController', function ($scope, $uibModalInstance, selectedTypes, selection, ExportFields, - Util) { + .controller('CustomExportController', + function ($scope, $uibModalInstance, selectedTypes, selection, ExportFields, Util) { + + if (selection.entryType) { + selectedTypes[selection.entryType] = []; + } + + console.log(selectedTypes, selection, Object.keys(selectedTypes).length); + $scope.processingDownload = false; - $scope.selected = { all: selection.all, types: [] }; - for (const key in selectedTypes) { - if (selectedTypes.hasOwnProperty(key)) { - $scope.selected.types.push(key); + $scope.selected = {all: selection.all, types: []}; + + for (const key in selectedTypes) { + if (selectedTypes.hasOwnProperty(key)) { + $scope.selected.types.push(key); + } } - } - // get the fields for display for user - $scope.fields = ExportFields.fields(); - $scope.sequence = { format: "FASTA", onePerFolder: false }; - $scope.general = {}; + // get the fields for display for user + $scope.fields = ExportFields.fields(); + $scope.sequence = {format: "FASTA", onePerFolder: false}; + $scope.general = {}; - $scope.onePerFolderClick = function () { - $scope.sequence.onePerFolder = !$scope.sequence.onePerFolder; - } + $scope.onePerFolderClick = function () { + $scope.sequence.onePerFolder = !$scope.sequence.onePerFolder; + } - $scope.canDisplayFieldSet = function (key) { - return (key === 'general' || selection.all || $scope.selected.types.indexOf(key.toUpperCase()) !== -1); - }; + $scope.canDisplayFieldSet = function (key) { + return (key === 'general' || selection.all || $scope.selected.types.indexOf(key.toUpperCase()) !== -1); + }; - $scope.customExport = function () { - $scope.processingDownload = false; - $scope.errorSubmitting = false; - selection.fields = []; + $scope.customExport = function () { + $scope.processingDownload = false; + $scope.errorSubmitting = false; + selection.fields = []; // get fields user wishes to export for (const subFields in $scope.fields) { @@ -48,17 +56,17 @@ angular.module('ice.entry.export.controller', []) } } - Util.post("rest/parts/custom", selection, function (success) { - $scope.processingDownload = true; - $scope.errorSubmitting = false; - }, { - sequenceFormat: $scope.sequence.format, - onePerFolder: $scope.sequence.onePerFolder - }, function (error) { - console.error(error); - $scope.processingDownload = false; - $scope.errorSubmitting = true; - }); + Util.post("rest/parts/custom", selection, function (success) { + $scope.processingDownload = true; + $scope.errorSubmitting = false; + }, { + sequenceFormat: $scope.sequence.format, + onePerFolder: $scope.sequence.onePerFolder + }, function (error) { + console.error(error); + $scope.processingDownload = false; + $scope.errorSubmitting = true; + }); }; } );