diff --git a/resources/queries/cds/learn_publicationExternalLinks.sql b/resources/queries/cds/learn_publicationExternalLinks.sql
new file mode 100644
index 000000000..8ea469501
--- /dev/null
+++ b/resources/queries/cds/learn_publicationExternalLinks.sql
@@ -0,0 +1,6 @@
+SELECT
+ elm.protocol_id,
+ el.*
+FROM cds.external_link el
+ LEFT JOIN cds.external_link_map elm ON el.cds_link_id = elm.cds_link_id
+WHERE elm.type = 'publication'
diff --git a/resources/queries/cds/learn_publicationdata.sql b/resources/queries/cds/learn_publicationdata.sql
index 53d61bc2d..f3cb72e0f 100644
--- a/resources/queries/cds/learn_publicationdata.sql
+++ b/resources/queries/cds/learn_publicationdata.sql
@@ -15,6 +15,7 @@
*/
SELECT
pd.publication_id,
+ pd.display_order,
doc.*
FROM cds.publicationDocument pd
LEFT JOIN document doc ON pd.document_id = doc.document_id
diff --git a/resources/queries/cds/learn_studyExternalLinks.sql b/resources/queries/cds/learn_studyExternalLinks.sql
new file mode 100644
index 000000000..c5a685e8c
--- /dev/null
+++ b/resources/queries/cds/learn_studyExternalLinks.sql
@@ -0,0 +1,6 @@
+SELECT
+ elm.protocol_id,
+ el.*
+FROM cds.external_link el
+ LEFT JOIN cds.external_link_map elm ON el.cds_link_id = elm.cds_link_id
+ WHERE elm.type = 'study'
diff --git a/resources/schemas/cds.xml b/resources/schemas/cds.xml
index a2567f58a..d0fd8d99d 100644
--- a/resources/schemas/cds.xml
+++ b/resources/schemas/cds.xml
@@ -729,6 +729,7 @@
+
+
+
\ No newline at end of file
diff --git a/resources/schemas/dbscripts/postgresql/cds-24.002-24.003.sql b/resources/schemas/dbscripts/postgresql/cds-24.002-24.003.sql
new file mode 100644
index 000000000..62dcc79b0
--- /dev/null
+++ b/resources/schemas/dbscripts/postgresql/cds-24.002-24.003.sql
@@ -0,0 +1,29 @@
+ALTER TABLE cds.import_PublicationDocument ADD COLUMN display_order INT;
+ALTER TABLE cds.PublicationDocument ADD COLUMN display_order INT;
+
+CREATE TABLE cds.external_link
+(
+ cds_link_id VARCHAR(250) NOT NULL,
+ link_label VARCHAR(250) NOT NULL,
+ link_url VARCHAR(250) NOT NULL,
+ link_type VARCHAR(100),
+ link_description VARCHAR(250),
+ container ENTITYID NOT NULL,
+
+ CONSTRAINT PK_external_links PRIMARY KEY (cds_link_id, container)
+);
+
+CREATE TABLE cds.external_link_map
+(
+ row_id SERIAL,
+ cds_link_id VARCHAR(250) NOT NULL,
+ protocol_id VARCHAR(250) NOT NULL,
+ type VARCHAR(100) NOT NULL,
+ container ENTITYID NOT NULL,
+
+ CONSTRAINT PK_external_link_map PRIMARY KEY (row_id),
+ CONSTRAINT UQ_external_link_map UNIQUE (cds_link_id, protocol_id, type, container),
+ CONSTRAINT FK_external_link_map_cds_link_id FOREIGN KEY (cds_link_id, container) REFERENCES cds.external_link (cds_link_id, container)
+);
+CREATE INDEX IX_external_link_map_cds_link_id ON cds.external_link_map(cds_link_id);
+
diff --git a/src/org/labkey/cds/CDSManager.java b/src/org/labkey/cds/CDSManager.java
index 98799b81a..90b5e549b 100644
--- a/src/org/labkey/cds/CDSManager.java
+++ b/src/org/labkey/cds/CDSManager.java
@@ -234,7 +234,10 @@ public void cleanContainer(Container c)
"alignment_run",
"allele_sequence",
"antibody_class",
- "header_source"
+ "header_source",
+
+ "external_link_map",
+ "external_link"
})
{
TableInfo t = dbSchema.getTable(s);
diff --git a/src/org/labkey/cds/CDSModule.java b/src/org/labkey/cds/CDSModule.java
index 0408272ed..159737c87 100644
--- a/src/org/labkey/cds/CDSModule.java
+++ b/src/org/labkey/cds/CDSModule.java
@@ -106,7 +106,7 @@ public String getName()
@Override
public @Nullable Double getSchemaVersion()
{
- return 24.002;
+ return 24.003;
}
@Override
diff --git a/src/org/labkey/cds/data/steps/CDSImportTask.java b/src/org/labkey/cds/data/steps/CDSImportTask.java
index a02aa04f0..434e0a39a 100644
--- a/src/org/labkey/cds/data/steps/CDSImportTask.java
+++ b/src/org/labkey/cds/data/steps/CDSImportTask.java
@@ -98,6 +98,10 @@ public class CDSImportTask extends ImportTask
new CDSImportCopyConfig("import_Virus_Metadata_All", "Virus_Metadata_All"),
new CDSImportCopyConfig("import_Virus_Lab_Id", "Virus_Lab_Id"),
new CDSImportCopyConfig("import_Virus_Synonym", "Virus_Synonym"),
+
+ // External repository links
+ new CDSImportCopyConfig("external_link"),
+ new CDSImportCopyConfig("external_link_map")
};
@Override
diff --git a/src/org/labkey/cds/view/template/ConnectorTemplate.jsp b/src/org/labkey/cds/view/template/ConnectorTemplate.jsp
index 898b2d46b..d37be19ca 100644
--- a/src/org/labkey/cds/view/template/ConnectorTemplate.jsp
+++ b/src/org/labkey/cds/view/template/ConnectorTemplate.jsp
@@ -356,6 +356,7 @@
srcPath + "/app/view/module/StudyResources.js",
srcPath + "/app/view/module/MabDetails.js",
srcPath + "/app/view/module/PublicationDetails.js",
+ srcPath + "/app/view/module/PublicationResources.js",
srcPath + "/app/view/module/ProductHeader.js",
srcPath + "/app/view/module/ProductOtherProducts.js",
srcPath + "/app/view/module/StudyHeader.js",
diff --git a/test/sampledata/dataspace/cdsimport/PublicationDocument.txt b/test/sampledata/dataspace/cdsimport/PublicationDocument.txt
index 59aded90d..80493df65 100644
--- a/test/sampledata/dataspace/cdsimport/PublicationDocument.txt
+++ b/test/sampledata/dataspace/cdsimport/PublicationDocument.txt
@@ -1,4 +1,4 @@
-publication_id document_id
-80 301
-136 300
-80 302
\ No newline at end of file
+publication_id document_id display_order
+80 301 2
+136 300
+80 302 1
\ No newline at end of file
diff --git a/test/sampledata/dataspace/cdsimport/external_link.txt b/test/sampledata/dataspace/cdsimport/external_link.txt
new file mode 100644
index 000000000..f979a015a
--- /dev/null
+++ b/test/sampledata/dataspace/cdsimport/external_link.txt
@@ -0,0 +1,4 @@
+cds_link_id link_label link_url link_type link_description
+cds_link_001 Vivli Data Repository http://vivli.com/data/CAVD725
+cds_link_002 Test Data Repository http://vivli.com/data/TEST test link not a real URL
+cds_link_003 GitHub https://github.com/labkey
\ No newline at end of file
diff --git a/test/sampledata/dataspace/cdsimport/external_link_map.txt b/test/sampledata/dataspace/cdsimport/external_link_map.txt
new file mode 100644
index 000000000..3e7e4153c
--- /dev/null
+++ b/test/sampledata/dataspace/cdsimport/external_link_map.txt
@@ -0,0 +1,7 @@
+cds_link_id protocol_id type
+cds_link_001 q2 study
+cds_link_002 q2 study
+cds_link_003 z117 study
+cds_link_001 136 publication
+cds_link_002 136 publication
+cds_link_003 80 publication
\ No newline at end of file
diff --git a/webapp/Connector/cube.js b/webapp/Connector/cube.js
index 1a230dc91..e9cc2e097 100644
--- a/webapp/Connector/cube.js
+++ b/webapp/Connector/cube.js
@@ -815,7 +815,12 @@ Ext4.define('Connector.cube.Configuration', {
type: 'publicationnonintegrateddata',
staticData: {
title: 'Publication data',
- instructions: 'Download files'
+ instructions: 'Download individual files'
+ }
+ },{
+ type: 'publicationresources',
+ staticData: {
+ title: 'Other publication resources'
}
},{
type: 'interactivereports',
diff --git a/webapp/Connector/extapp.lib.xml b/webapp/Connector/extapp.lib.xml
index 4f4e37b45..4e4e6e22f 100644
--- a/webapp/Connector/extapp.lib.xml
+++ b/webapp/Connector/extapp.lib.xml
@@ -211,6 +211,7 @@
+
diff --git a/webapp/Connector/src/app/model/Publication.js b/webapp/Connector/src/app/model/Publication.js
index a979d6a8c..6ce88d12d 100644
--- a/webapp/Connector/src/app/model/Publication.js
+++ b/webapp/Connector/src/app/model/Publication.js
@@ -39,6 +39,7 @@ Ext.define('Connector.app.model.Publication', {
{name: 'studies', convert : Connector.model.Filter.asArray},
{name: 'interactive_reports', convert : Connector.model.Filter.asArray},
{name: 'curated_groups', convert : Connector.model.Filter.asArray},
- {name: 'data_types_available', convert : Connector.model.Filter.asArray}
+ {name: 'data_types_available', convert : Connector.model.Filter.asArray},
+ {name: 'external_links', convert : Connector.model.Filter.asArray}
]
});
\ No newline at end of file
diff --git a/webapp/Connector/src/app/model/StudyOverview.js b/webapp/Connector/src/app/model/StudyOverview.js
index c93c68f48..d6306318f 100644
--- a/webapp/Connector/src/app/model/StudyOverview.js
+++ b/webapp/Connector/src/app/model/StudyOverview.js
@@ -123,6 +123,7 @@ Ext.define('Connector.app.model.StudyOverview', {
{name: 'interactive_reports', convert : Connector.model.Filter.asArray},
{name: 'curated_groups', convert : Connector.model.Filter.asArray},
{name: 'data_types_available', convert : Connector.model.Filter.asArray},
- {name: 'data_available'}
+ {name: 'data_available'},
+ {name: 'external_links', convert : Connector.model.Filter.asArray}
]
});
\ No newline at end of file
diff --git a/webapp/Connector/src/app/store/Publication.js b/webapp/Connector/src/app/store/Publication.js
index 9628ec356..bbb8a7df6 100644
--- a/webapp/Connector/src/app/store/Publication.js
+++ b/webapp/Connector/src/app/store/Publication.js
@@ -29,6 +29,7 @@ Ext.define('Connector.app.store.Publication', {
this.publicationDocuments = undefined;
this.publicationReportsData = undefined;
this.publicationCuratedGroupData = undefined;
+ this.external_links = undefined;
this.loadAccessibleStudies(this._onLoadComplete, this); // populate this.accessibleStudies
@@ -71,6 +72,13 @@ Ext.define('Connector.app.store.Publication', {
success: this.onLoadPublicationCuratedGroup,
scope: this
});
+
+ LABKEY.Query.selectRows({
+ schemaName: 'cds',
+ queryName: 'learn_publicationExternalLinks',
+ success: this.onLoadExternalLinks,
+ scope: this
+ });
},
onLoadPublications: function (data) {
@@ -103,6 +111,11 @@ Ext.define('Connector.app.store.Publication', {
this._onLoadComplete();
},
+ onLoadExternalLinks : function(externalLinks) {
+ this.external_links = externalLinks.rows;
+ this._onLoadComplete();
+ },
+
_onLoadComplete : function() {
if (Ext.isDefined(this.publicationData)
&& Ext.isDefined(this.studyData)
@@ -111,6 +124,7 @@ Ext.define('Connector.app.store.Publication', {
&& Ext.isDefined(this.publicationDocuments)
&& Ext.isDefined(this.publicationReportsData)
&& Ext.isDefined(this.publicationCuratedGroupData)
+ && Ext.isDefined(this.external_links)
&& this.isLoadComplete()) {
this.publicationData.sort(function(row1, row2) {
@@ -170,6 +184,7 @@ Ext.define('Connector.app.store.Publication', {
}, this).map(function(doc) {
return {
publication_id: doc.publication_id,
+ display_order : doc.display_order || 0,
document_id: doc.document_id,
label: doc.label,
fileName: doc.filename,
@@ -191,6 +206,20 @@ Ext.define('Connector.app.store.Publication', {
publicationMap[doc.publication_id].push(doc);
}, this);
+ // sort the publication docs according to display order (ascending)
+ Ext.Object.each(publicationMap, function(key, publications){
+ publications.sort(function(a, b){
+ return a.display_order - b.display_order;
+ });
+ });
+
+ // map any external links to each publication
+ var externalLinkMap = {};
+ Ext.each(this.external_links, function (link) {
+ externalLinkMap[link.protocol_id] = externalLinkMap[link.protocol_id] || [];
+ externalLinkMap[link.protocol_id].push(link);
+ }, this);
+
var savedReports = [];
for (var i=0; i < this.publicationReportsData.length; i++) {
var id = this.publicationReportsData[i].cds_report_id.toString();
@@ -243,6 +272,9 @@ Ext.define('Connector.app.store.Publication', {
publication.publication_data_count = publication.publication_data.length;
publication.data_availability = publication.publication_data.length > 0;
+ // external links
+ publication.external_links = externalLinkMap[publication.publication_id] || [];
+
//saved reports
var savedRep = savedReports.filter(function (value) {
return value.publication_id.toString() === publication.publication_id;
@@ -271,6 +303,7 @@ Ext.define('Connector.app.store.Publication', {
this.publicationReportsData = undefined;
this.savedReportsData = [];
this.publicationCuratedGroupData = undefined;
+ this.external_links = [];
this.loadRawData(publications);
this.fireEvent('dataloaded');
diff --git a/webapp/Connector/src/app/store/StudyOverview.js b/webapp/Connector/src/app/store/StudyOverview.js
index fd7393230..003f26fc4 100644
--- a/webapp/Connector/src/app/store/StudyOverview.js
+++ b/webapp/Connector/src/app/store/StudyOverview.js
@@ -35,12 +35,16 @@ Ext.define('Connector.app.store.StudyOverview', {
this.assayIdentifiers = undefined;
this.studyReportsData = undefined;
this.studyCuratedGroupData = undefined;
+ this.external_links = undefined;
this.loadAccessibleStudies(this._onLoadComplete, this); // populate this.accessibleStudies
LABKEY.Query.selectRows({
schemaName: 'cds.metadata',
queryName: 'study',
+ filterArray: [
+ LABKEY.Filter.create('study_name', id, LABKEY.Filter.Types.EQUALS)
+ ],
success: this.onLoadStudies,
scope: this
});
@@ -135,6 +139,15 @@ Ext.define('Connector.app.store.StudyOverview', {
success: this.onNILoadDocuments,
scope: this
});
+ LABKEY.Query.selectRows({
+ schemaName: 'cds',
+ queryName: 'learn_studyExternalLinks',
+ filterArray: [
+ LABKEY.Filter.create('protocol_id', id, LABKEY.Filter.Types.EQUALS)
+ ],
+ success: this.onLoadStudyExternalLinks,
+ scope: this
+ });
},
onLoadStudies : function(studyData) {
@@ -199,6 +212,11 @@ Ext.define('Connector.app.store.StudyOverview', {
this._onLoadComplete();
},
+ onLoadStudyExternalLinks : function(externalLinks) {
+ this.external_links = externalLinks.rows;
+ this._onLoadComplete();
+ },
+
_onLoadComplete: function () {
if (Ext.isDefined(this.studyData)
&& Ext.isDefined(this.productData)
@@ -213,6 +231,7 @@ Ext.define('Connector.app.store.StudyOverview', {
&& Ext.isDefined(this.assayIdentifiers)
&& Ext.isDefined(this.studyReportsData)
&& Ext.isDefined(this.studyCuratedGroupData)
+ && Ext.isDefined(this.external_links)
&& this.isLoadComplete()) {
var studies = [], products, productNames, productClasses;
var relationshipOrderList = this.relationshipOrderData.map(function(relOrder) {
@@ -544,13 +563,11 @@ Ext.define('Connector.app.store.StudyOverview', {
study.data_types_available = this.getDataTypesAvailable(study);
study.data_available = (study.assays_added_count > 0 || study.ni_assays_added_count > 0 || study.pub_available_data_count > 0) ? 'Data added' : 'Data not added';
+ // additional data repositories
+ study.external_links = this.external_links;
studies.push(study);
}, this);
- studies.sort(function(studyA, studyB) {
- return Connector.model.Filter.sorters.natural(studyA.label, studyB.label);
- });
-
this.studyData = undefined;
this.assayData = undefined;
this.productData = undefined;
@@ -563,6 +580,7 @@ Ext.define('Connector.app.store.StudyOverview', {
this.studyReportsData = undefined;
this.savedReportsData = [];
this.studyCuratedGroupData = undefined;
+ this.external_links = [];
this.loadRawData(studies);
this.dataLoaded = true;
diff --git a/webapp/Connector/src/app/view/module/NonIntegratedDataAvailability.js b/webapp/Connector/src/app/view/module/NonIntegratedDataAvailability.js
index 71abd1e17..faafd828e 100644
--- a/webapp/Connector/src/app/view/module/NonIntegratedDataAvailability.js
+++ b/webapp/Connector/src/app/view/module/NonIntegratedDataAvailability.js
@@ -303,15 +303,18 @@ Ext.define('Connector.view.module.PublicationNonIntegratedData', {
return new Ext4.XTemplate(
'',
'',
- '',
- '',
- '',
+ '',
+ '',
+ '',
+ ' ',
+ ' | ',
+ '',
'{label:htmlEncode}',
' {suffix} ',
' ',
- '',
- ' | ',
- ' ',
+ ' | ',
+ '
',
+ '',
'
',
'');
}
diff --git a/webapp/Connector/src/app/view/module/PublicationResources.js b/webapp/Connector/src/app/view/module/PublicationResources.js
new file mode 100644
index 000000000..5c189d72d
--- /dev/null
+++ b/webapp/Connector/src/app/view/module/PublicationResources.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016-2024 LabKey Corporation
+ *
+ * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+ */
+Ext.define('Connector.view.module.PublicationResources', {
+
+ xtype : 'app.module.publicationresources',
+
+ extend : 'Connector.view.module.BaseModule',
+
+ tpl : new Ext.XTemplate(
+ '',
+ '{title_publication_resources:htmlEncode}
',
+ '',
+ '',
+ '',
+ ''
+ ),
+
+ initComponent : function() {
+ var data = this.initialConfig.data.model.data;
+ data['title_publication_resources'] = this.initialConfig.data.title;
+
+ this.update(data);
+ }
+});
diff --git a/webapp/Connector/src/app/view/module/StudyResources.js b/webapp/Connector/src/app/view/module/StudyResources.js
index 08e10b46b..df096bcc7 100644
--- a/webapp/Connector/src/app/view/module/StudyResources.js
+++ b/webapp/Connector/src/app/view/module/StudyResources.js
@@ -46,7 +46,12 @@ Ext.define('Connector.view.module.StudyResources', {
'View research specimens in repository 
',
'',
'',
- ''
+ '',
+ '',
+ '',
+ ''
),
initComponent : function() {
var data = this.initialConfig.data.model.data;
diff --git a/webapp/Connector/src/constant/ModuleViewsLookup.js b/webapp/Connector/src/constant/ModuleViewsLookup.js
index 8c21c8131..1f86b95cd 100644
--- a/webapp/Connector/src/constant/ModuleViewsLookup.js
+++ b/webapp/Connector/src/constant/ModuleViewsLookup.js
@@ -24,6 +24,7 @@ Ext.define('Connector.constant.ModuleViewsLookup', {
studymabs: 'app.module.studymabs',
mabdetails: 'app.module.mabdetails',
publicationdetails: 'app.module.publicationdetails',
+ publicationresources: 'app.module.publicationresources',
assayheader: 'app.module.assayheader',
assayanalytelist: 'app.module.assayanalytelist',
variablelist: 'app.module.variablelist',