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
onclick
event tosearchItemsToDownload(true);
- Set
- Group
- Set
id
todownloadList
- Set
renderMode
todiv
- Set
- Client Side Script (See example below)
- Server Side Script (see example below)
- Group
- Set
id
todialogDownload
- Set
renderMode
todiv
- 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
searchItemsToDownload
function to get the download list from the IRB Exchange and dislay it. - The
populateTable
function forms HTML based on data from the IRB Exchange and displays it to the page. It puts the HTML into thedownloadList
group and it contains the HTML necessary to display the Download button that will download the submission when clicked. - The
searchItemsToDownload
function gets the download list from the IRB Exchange and calls thepopulateTable
function to display it. This makes a request to theDownloadListRequest
Site Designer page that gets the data from the IRB Exchange. While the request is being made, thedialogDownload
group is turned into a jQuery dialog to display a progress message. - The
openDownloadStudyDialog
function 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 thedialogDownload
group 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
downloadStudy
function turns thedialogDownload
group into a jQuery progress indicator while it does its work to download the chosen study. It makes a request to theDownloadListRequest
Site 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.downloadDocument
method to download the document content in a separate transaction in order to avoid timeouts. - The
downloadRni
function works similarly to thedownloadStudy
function, 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
DownloadListRequest
Site 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
GetDownloadList
action 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.GetDownloadList
method to get the data from the IRB Exchange. - The
DownloadStudy
action downloads a chosen study from the IRB Exchange. It calls theIrbExchange.GetStudy
method to get the data from the IRB Exchange and passes it off to a call to the_IRBSubmission.createExternalStudyFromExchange
method to have the study created and set all the data on it. - The
DownloadRni
action downloads a reportable new information submission from the IRB Exchange. It calls theIrbExchange.GetRni
method to get the data from the IRB Exchange and passes it off to a call to the_IRBSubmission.createExternalRniFromExchange
method to have the reportable new information submission created and set all the data on it.