Download List
In Huron IRB, we implemented a list control on the IRB page under the IRB Exchange tab to facilitate downloads of studies and reportable new information submissions. This list will show all submissions available to download for all IRB Exchange accounts setup on IRB offices.
What's Involved
- Adding a Site Designer page to implement the user interface controls and client side logic for downloading submissions.
- Adding a Site Designer page to handle server requests from the download list controls and server side logic.
Implementing the Client Side Controls
Create a Site Designer page at Applications:\IRB\Exchange\DownloadList.
Create the following controls in the following order:
- Push Button
- Set
onclickevent tosearchItemsToDownload(true);
- Set
- Group
- Set
idtodownloadList - Set
renderModetodiv
- Set
- Client Side Script (See example below)
- Server Side Script (see example below)
- Group
- Set
idtodialogDownload - Set
renderModetodiv
- Set
Client Side Script Example
window.onload = function() {
searchItemsToDownload(true);
};
function populateTable(data){
var rows = "";
var items = data.items;
var resultCount = items.length;
// Clear out the existing table values
$("#downloadList").empty();
if(resultCount > 0) {
var html = "<table class='DataTable' border='0' cellspacing='0' cellpadding='3'>";
html += "<thead><td class='DisplayHead' width='null'>Submission Type</td><td class='DisplayHead' width='null'>Submission Name</td>";
html += "<td class='DisplayHead' width='null'>Institution</td>";
html += "<td class='DisplayHead' width='null'>Lead Principal Investigator</td>";
html += "<td class='DisplayHead' width='null'>pSite Principal Investigator</td></thead><tbody>";
var row;
var tr = "<tr class='drsv-row'>";
var td = "<td class='List' selectedClass='ListSelected' align='left' style='vertical-align: center;'>";
var button = "<input class='Button2' type='button' value='Download' onclick=\"";
$.each(items, function (i, item) {
if (item.submissionType == "reportable-new-information") {
row = tr + td + "RNI</td>";
} else {
row = tr + td + "Study</td>";
}
row += td + item.name + "</td>";
row += td + item.owner.name + "</td>";
row += td + item.principalInvestigator + "</td>";
row += td + item.sitePrincipalInvestigator + "</td>";
if (item.submissionType == "reportable-new-information") {
row += td + button + "downloadRni('" + item.containerId + "','" + item.exchangeRef + "','" + item.accountName + "')\"></td></tr>";
} else if(item.clientFunctionName == "downloadStudy") {
row += td + button + item.clientFunctionName + "('" + item.exchangeRef + "','" + item.adminOffices[0] + "')\"></td></tr>";
} else {
row += td + button + item.clientFunctionName + "('" + item.exchangeRef + "','" + item.adminOffices + "')\"></td></tr>";
}
rows += row;
});
html += rows;
html += "</tbody></table>";
} else {
html = "<p style='text-align: center'>There are no items available to download</p>";
}
$("#downloadList").append(html);
}
function searchItemsToDownload(showDialog)
{
// Display a dialog informing the user of progress
if(showDialog == true) {
jQuery(function () {
jQuery("#dialogDownload").dialog({
buttons: {
},
title: "Getting list of items to download..."
});
jQuery("#dialogDownload").html("Getting list of items to download. This will take a few moments...");
});
}
var searchValue = jQuery("#searchBox").val();
if(searchValue == undefined) {
searchValue = "";
}
var restAPI = requestUrl + "?action=GetDownloadList&searchBox=" + searchValue;
var noErrorWasThrown = true;
var jqxhr = jQuery.getJSON(restAPI, {})
.done(function(data) {
if(showDialog == true) {
jQuery(function () {
jQuery("#dialogDownload").dialog("close");
});
}
console.log(restAPI);
var itemCount = data.items.length;
if(itemCount == undefined) {
// Handle Error that may have come back
noErrorWasThrown = false;
var ex = data.exception;
console.log(ex);
jQuery('#dialogDownload').html(ex);
}
if(noErrorWasThrown){
console.log("download list retrieved. " + itemCount + " results returned");
populateTable(data);
}
})
.fail(function() {
console.log("search failed");
})
.always(function() {
console.log("search complete");
});
}
function openDownloadStudyDialog(containerId, adminOffices)
{
// Display a dialog asking the user to select admin office
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
Download: function() {
if(jQuery("#adminOfficeSelectControl").val() == null) {
alert("Must select an admin office");
return;
}
downloadStudy(containerId, jQuery("#adminOfficeSelectControl").val());
}
},
title: "Download Study"
});
var adminOfficeSelect = "<select ID='adminOfficeSelectControl'>";
var currentAdminOfficeId;
var adminOfficesArray = adminOffices.split(",");
for(var i = 0; i < adminOfficesArray.length; i++) {
currentAdminOfficeId = adminOfficesArray[i];
adminOfficeSelect += "<option value='" + currentAdminOfficeId + "'>" + currentAdminOfficeId + "</option>";
}
adminOfficeSelect += "</select>";
jQuery("#dialogDownload").html("Ready to download study.<br><br>Choose an admin office: " + adminOfficeSelect);
} );
}
function downloadStudy(containerId, adminOfficeId)
{
// Display a dialog informing the user of progress
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
},
title: "Downloading Study..."
});
jQuery("#dialogDownload").html("Downloading the study. This will take a few moments...");
} );
// Download the study and when done change the dialog content to reflect
var restAPI = requestUrl + "?action=DownloadStudy&containerId=" + containerId + "&adminOfficeId=" + adminOfficeId;
var jqxhr = jQuery.getJSON(restAPI, {})
.done(function(data) {
var downloadDocumentsDetails = {};
downloadDocumentsDetails["study"] = data.study;
var documentsToDownloadArray = data.documentsToDownloadArray;
var studyLink = data.studyLink;
jQuery(function(){
if(typeof downloadDocumentsScope == "undefined") {
downloadDocumentsScope = {};
}
downloadDocumentsScope.handleFailure = function(jqXHR, data, textStatus){
var errorString = '<p class="Error">Error thrown attempting to download documents from IRB Exchange. Please contact a system administrator. ' + data + "</p>";
jQuery('#dialogDownload').append(errorString);
jQuery("body").css("cursor", "default");
}
downloadDocumentsScope.downloadDocument = function(index){
downloadDocumentsDetails["documentPoRef"] = downloadDocumentsScope.documentsToDownload[index].document;
downloadDocumentsDetails["entity"] = downloadDocumentsScope.documentsToDownload[index].entity;
downloadDocumentsDetails["attributePath"] = downloadDocumentsScope.documentsToDownload[index].attributePath;
downloadDocumentsDetails["exchangeRef"] = downloadDocumentsScope.documentsToDownload[index].exchangeRef;
PortalTools.callRemoteMethod("ClickIRBRemoteMethods.downloadDocument", JSON.stringify(downloadDocumentsDetails)).done(function(data, textStatus, jqXHR){
if(data != "success"){
downloadDocumentsScope.handleFailure(jqXHR, data, textStatus);
return;
}
downloadDocumentsScope.downloadedCount++;
jQuery('#dialogDownload').html('downloaded ' + downloadDocumentsScope.downloadedCount + ' of ' + downloadDocumentsScope.documentsToDownload.length + ' ... ');
if(downloadDocumentsScope.downloadedCount == downloadDocumentsScope.documentsToDownload.length){
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
Close: function() {
jQuery( this ).dialog( "close" );
}
},
title: "Study Downloaded"
});
} );
jQuery("body").css("cursor", "default");
jQuery("#dialogDownload").html("Study downloaded successfully!<br><br>You may click the link below to navigate to the new external study:<br><br>" + studyLink);
searchItemsToDownload(false);
}
}).fail(downloadDocumentsScope.handleFailure);
}
downloadDocumentsScope.documentsToDownload = JSON.parse(documentsToDownloadArray, null);
var documentsToDownloadCount = downloadDocumentsScope.documentsToDownload.length;
if (documentsToDownloadCount > 0) {
jQuery("#dialogDownload").html('<strong>' + documentsToDownloadCount + ' documents still to be downloaded</strong>');
jQuery("body").css("cursor", "progress");
downloadDocumentsScope.downloadedCount = 0;
for (var i = 0; i < documentsToDownloadCount; i++) {
downloadDocumentsScope.downloadDocument(i);
}
} else {
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
Close: function() {
jQuery( this ).dialog( "close" );
}
},
title: "Study Downloaded"
});
} );
jQuery("body").css("cursor", "default");
jQuery("#dialogDownload").html("Study downloaded successfully!<br><br>You may click the link below to navigate to the new external study:<br><br>" + studyLink);
searchItemsToDownload(false);
}
});
})
.fail(function() {
console.log("download failed");
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
Close: function() {
jQuery( this ).dialog( "close" );
}
},
title: "Download Study Failed"
});
} );
jQuery("#dialogDownload").html("Failed to download study.<br><br>Contact your system administrator.");
})
.always(function() {
console.log("download complete");
});
}
function downloadRni(containerId, rniItemId, accountName)
{
// Display a dialog informing the user of progress
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
},
title: "Downloading RNI..."
});
jQuery("#dialogDownload").html("Downloading the RNI. This will take a few moments...");
} );
// Download the RNI and when done change the dialog content to reflect
var restAPI = requestUrl + "?action=DownloadRni&containerId=" + containerId + "&rniItemId=" + rniItemId + "&accountName=" + accountName;
var noErrorWasThrown = true;
var jqxhr = jQuery.getJSON(restAPI, {})
.done(function(data) {
var downloadDocumentsDetails = {};
downloadDocumentsDetails["study"] = data.study;
var documentsToDownloadArray = data.documentsToDownloadArray;
var rniLink = data.rniLink;
jQuery(function() {
if (typeof downloadDocumentsScope == "undefined") {
downloadDocumentsScope = {};
}
downloadDocumentsScope.handleFailure = function (jqXHR, data, textStatus) {
var errorString = '<p class="Error">Error thrown attempting to download documents from IRB Exchange. Please contact a system administrator. ' + data + "</p>";
jQuery('#dialogDownload').append(errorString);
jQuery("body").css("cursor", "default");
}
downloadDocumentsScope.downloadDocument = function (index) {
downloadDocumentsDetails["documentPoRef"] = downloadDocumentsScope.documentsToDownload[index].document;
downloadDocumentsDetails["entity"] = downloadDocumentsScope.documentsToDownload[index].entity;
downloadDocumentsDetails["attributePath"] = downloadDocumentsScope.documentsToDownload[index].attributePath;
downloadDocumentsDetails["exchangeRef"] = downloadDocumentsScope.documentsToDownload[index].exchangeRef;
PortalTools.callRemoteMethod("ClickIRBRemoteMethods.downloadDocument", JSON.stringify(downloadDocumentsDetails)).done(function (data, textStatus, jqXHR) {
if (data != "success") {
downloadDocumentsScope.handleFailure(jqXHR, data, textStatus);
return;
}
downloadDocumentsScope.downloadedCount++;
jQuery('#dialogDownload').html('downloaded ' + downloadDocumentsScope.downloadedCount + ' of ' + downloadDocumentsScope.documentsToDownload.length + ' ... ');
if (downloadDocumentsScope.downloadedCount == downloadDocumentsScope.documentsToDownload.length) {
jQuery(function () {
jQuery("#dialogDownload").dialog({
buttons: {
Close: function () {
jQuery(this).dialog("close");
}
},
title: "RNI Downloaded"
});
});
jQuery("body").css("cursor", "default");
jQuery("#dialogDownload").html("RNI downloaded successfully!<br><br>You may click the link below to navigate to the new external RNI:<br><br>" + rniLink);
searchItemsToDownload(false);
}
}).fail(downloadDocumentsScope.handleFailure);
}
downloadDocumentsScope.documentsToDownload = JSON.parse(documentsToDownloadArray, null);
var documentsToDownloadCount = downloadDocumentsScope.documentsToDownload.length;
if (documentsToDownloadCount > 0) {
jQuery("#dialogDownload").html('<strong>' + documentsToDownloadCount + ' documents still to be downloaded</strong>');
jQuery("body").css("cursor", "progress");
downloadDocumentsScope.downloadedCount = 0;
for (var i = 0; i < documentsToDownloadCount; i++) {
downloadDocumentsScope.downloadDocument(i);
}
} else {
jQuery(function () {
jQuery("#dialogDownload").dialog({
buttons: {
Close: function () {
jQuery(this).dialog("close");
}
},
title: "RNI Downloaded"
});
});
jQuery("body").css("cursor", "default");
jQuery("#dialogDownload").html("RNI downloaded successfully!<br><br>You may click the link below to navigate to the new external RNI:<br><br>" + rniLink);
searchItemsToDownload(false);
}
})
})
.fail(function() {
console.log("download failed");
jQuery( function() {
jQuery( "#dialogDownload" ).dialog({
buttons: {
Close: function() {
jQuery( this ).dialog( "close" );
}
},
title: "Download RNI Failed"
});
} );
jQuery("#dialogDownload").html("Failed to download RNI.<br><br>Contact your system administrator.");
})
.always(function() {
console.log("download complete");
});
}
Technical notes about this script example:
- When the page loads, it calls
searchItemsToDownloadfunction to get the download list from the IRB Exchange and dislay it. - The
populateTablefunction forms HTML based on data from the IRB Exchange and displays it to the page. It puts the HTML into thedownloadListgroup and it contains the HTML necessary to display the Download button that will download the submission when clicked. - The
searchItemsToDownloadfunction gets the download list from the IRB Exchange and calls thepopulateTablefunction to display it. This makes a request to theDownloadListRequestSite Designer page that gets the data from the IRB Exchange. While the request is being made, thedialogDownloadgroup is turned into a jQuery dialog to display a progress message. - The
openDownloadStudyDialogfunction runs when the user clicks the Download button for a study where more than one IRB office is available to assign to it. It turns thedialogDownloadgroup into a jQuery dialog that has an IRB office chooser and Download button on it. When the user clicks the Download button, it validates that they selected an IRB office and initiates the download operation. - The
downloadStudyfunction turns thedialogDownloadgroup into a jQuery progress indicator while it does its work to download the chosen study. It makes a request to theDownloadListRequestSite Designer page to get the study's data. On the callback function, it takes JSON metadata returned and uses it to download any documents on the study. For each document, it makes a remote call to theClickIRBRemoteMethods.downloadDocumentmethod to download the document content in a separate transaction in order to avoid timeouts. - The
downloadRnifunction works similarly to thedownloadStudyfunction, but for downloading reportable new information submissions.
Server Side Script Example
function generateHtml(sch)
{
var baseUrl = sch.fullUrlFromUnsUrl(""); //Right now this points to the local machine
//construct the url to be used for restfull calls.
var html = "<script type='text/javascript'>";
html += "var requestUrl = \""+ baseUrl +"Applications/IRB/Exchange/DownloadListRequest\";\n";
html += "</script>";
return html;
}
Technical note about this script example:
- This script is needed to put HTML on the page that runs a client side script that sets a variable with the value of the request URL to the
DownloadListRequestSite Designer page.
Implementing the Request Handler Page
Create a Site Designer page at Applications:\IRB\Exchange\DownloadListRequest. Define the httpResponse script for the Page control.
httpResponse Script Example
function httpResponse(sch) {
try {
// Strings representing the different actions available
var GET_DOWNLOAD_LIST_ACTION_STR = "GetDownloadList";
var DOWNLOAD_STUDY_ACTION_STR = "DownloadStudy";
var DOWNLOAD_RNI_ACTION_STR = "DownloadRni";
// Get the action string so we know what action is desired
sch.addHttpHeader("Content-Type","application/json");
var html = {};
var action = sch.QueryString("action");
switch(action) {
case GET_DOWNLOAD_LIST_ACTION_STR:
var searchStr = sch.QueryString("searchBox");
// Get itemsToDownload from exchange filtered to name in search textbox that haven't been downloaded yet
var activeAdminOffices = ApplicationEntity.getResultSet("_AdminOffice").query("customAttributes.disabled = False and customAttributes.officeType.ID = 'ID00000001' and customAttributes.iRBExchangeAccountName is not null");
var activeAdminOfficesElements = activeAdminOffices.elements();
var activeAdminOfficesCount = activeAdminOfficesElements.count();
var exchangeClient, itemsToDownload, items, currentAccountName, currentAdminOffice, adminOfficesJson, adminOfficesForAccountName, adminOfficesForAccountNameCount, isUniqueAccountName;
html.items = [];
var accountNames = [];
for(var i = 1; i <= activeAdminOfficesCount; i++) {
currentAdminOffice = activeAdminOfficesElements.item(i);
currentAccountName = currentAdminOffice.getQualifiedAttribute("customAttributes.iRBExchangeAccountName");
if(currentAccountName != null) {
isUniqueAccountName = true;
for(var j = 0; j < accountNames.length; j++) {
if(accountNames[j] == currentAccountName) {
isUniqueAccountName = false;
}
}
if(isUniqueAccountName == true) {
accountNames.push(currentAccountName);
exchangeClient = ClickIRBUtils.getExchangeClient(currentAccountName);
itemsToDownload = exchangeClient.GetDownloadList();
items = JSON.parse(itemsToDownload, null);
adminOfficesJson = [];
adminOfficesForAccountName = activeAdminOffices.query("customAttributes.iRBExchangeAccountName = '" + currentAccountName + "'").elements();
adminOfficesForAccountNameCount = adminOfficesForAccountName.count();
for (var j = 1; j <= adminOfficesForAccountNameCount; j++) {
adminOfficesJson.push(adminOfficesForAccountName.item(j).ID);
}
for (var j = 0; j < items.length; j++) {
items[j].accountName = currentAccountName;
items[j].adminOffices = adminOfficesJson;
if (adminOfficesForAccountNameCount > 1) {
items[j].clientFunctionName = "openDownloadStudyDialog";
} else {
items[j].clientFunctionName = "downloadStudy";
}
html.items.push(items[j]);
}
}
}
}
break;
case DOWNLOAD_STUDY_ACTION_STR:
var containerId = sch.QueryString("containerId");
var adminOfficeId = sch.QueryString("adminOfficeId");
// Get rni item from Exchange for the rni selected to download
var adminOfficeMatches = ApplicationEntity.getResultSet("_AdminOffice").query("ID = '" + adminOfficeId + "'").elements();
if(adminOfficeMatches.count() != 1) {
throw new Error("Unexpected count of _AdminOffice matching ID " + adminOfficeId);
}
var adminOfficeAccountName = adminOfficeMatches.item(1).getQualifiedAttribute("customAttributes.iRBExchangeAccountName");
var exchangeClient = ClickIRBUtils.getExchangeClient(adminOfficeAccountName);
var studyJsonStr = exchangeClient.GetStudy(null, containerId);
if(studyJsonStr == null) {
throw new Error("Study JSON returned from Exchange GetStudy method unexpectedly null");
}
var studyJson = JSON.parse(studyJsonStr, null);
studyJson.adminOfficeId = adminOfficeId;
// Create the external rni and local site, with data, including container ID of rni
html = _IRBSubmission.createExternalStudyFromExchange(studyJson, exchangeClient);
var study = wom.getEntityFromString(html.study);
html.studyLink = "<a style='text-decoration: underline;' href='" + study.getUrl() + "'>" + study.name + "</a>";
break;
case DOWNLOAD_RNI_ACTION_STR:
var containerId = sch.QueryString("containerId");
var rniItemId = sch.QueryString("rniItemId");
var accountName = sch.QueryString("accountName");
// Get RNI item from Exchange for the RNI selected to download
var exchangeClient = ClickIRBUtils.getExchangeClient(accountName);
var rniJsonStr = exchangeClient.GetRni(null, containerId, null, rniItemId);
if(rniJsonStr == null) {
throw new Error("RNI JSON returned from Exchange GetRni method unexpectedly null");
}
var rniJson = JSON.parse(rniJsonStr, null);
// Create the RNI
html = _IRBSubmission.createExternalRniFromExchange(rniJson, exchangeClient);
var rni = wom.getEntityFromString(html.rni);
// Put a link to the external rni in the HTML
html.rniLink = "<a style='text-decoration: underline;' href='" + rni.getUrl() + "'>" + rni.name + "</a>";
break;
default:
throw new Error("Site Designer page Applications: IRB/DownloadList httpResponse script: Unexpected action string received: " + action);
break;
}
sch.appendHTML(JSON.stringify(html, null, null));
} catch (e) {
wom.log("EXCEPTION /Applications/IRB/Exchange/DownloadList " + e.description);
var errorJson = {"exception": e.description};
sch.appendHtml(JSON.stringify(errorJson, null, null));
}
return 0;
}
Technical notes about this script example:
- The different actions required of this script are divided using a switch statement.
- The
GetDownloadListaction gets the download list from the IRB Exchange. It collects the unique IRB Exchange accounts configured on active IRB offices to get its data. It calls theIrbExchange.GetDownloadListmethod to get the data from the IRB Exchange. - The
DownloadStudyaction downloads a chosen study from the IRB Exchange. It calls theIrbExchange.GetStudymethod to get the data from the IRB Exchange and passes it off to a call to the_IRBSubmission.createExternalStudyFromExchangemethod to have the study created and set all the data on it. - The
DownloadRniaction downloads a reportable new information submission from the IRB Exchange. It calls theIrbExchange.GetRnimethod to get the data from the IRB Exchange and passes it off to a call to the_IRBSubmission.createExternalRniFromExchangemethod to have the reportable new information submission created and set all the data on it.