Merge pull request #350 from okriuchykhin/ok_SCI_742_v1

Implemented protocol export through Zip::OutputStream [SCI-742]
This commit is contained in:
okriuchykhin 2017-01-23 17:49:05 +01:00 committed by GitHub
commit fe329d09cc
8 changed files with 339 additions and 463 deletions

View file

@ -56,6 +56,7 @@ gem 'nokogiri' # HTML/XML parser
gem 'sneaky-save', git: 'git://github.com/einzige/sneaky-save.git'
gem 'rails_autolink', '~> 1.1', '>= 1.1.6'
gem 'delayed_paperclip'
gem 'rubyzip'
gem 'paperclip', '~> 4.3' # File attachment, image attachment library
gem 'aws-sdk', '~> 2.2.8'

View file

@ -1,5 +1,4 @@
//= require protocols/import_export/import
//= require protocols/import_export/export
//= require comments
//= require datatables
@ -17,7 +16,6 @@ function init() {
initLoadFromRepository();
initRefreshStatusBar();
initImport();
initExport();
Comments.bindNewElement();
Comments.initialize();
initTutorial();
@ -510,11 +508,4 @@ function initImport() {
});
}
function initExport() {
var exportBtn = $("[data-action='export']");
exportBtn.on("click", function() {
exportProtocols([ $(this).attr("data-id") ]);
});
}
init();

View file

@ -1,372 +0,0 @@
//= require jszip.min.js
function exportProtocols(protocol_ids) {
/*********************************************/
/* INNER FUNCTIONS */
/*********************************************/
// Custom StringBuilder 'object'
function StringBuilder() {
this.tokens = [];
this.add = function(token) {
this.tokens.push(token);
return this;
};
this.nl = function() {
this.tokens.push('\n');
return this;
};
this.build = function() {
var str = this.tokens.join("");
this.tokens = null;
return str;
};
}
function getGuid(id) {
var str1 = "00000000-0000-";
var str2 = id.toString();
for (var i = str2.length; i < 19; i++) {
str2 = "0" + str2;
}
str2 = "4" + str2;
var str2n = str2.slice(0, 4) + "-" + str2.slice(4, 8) + "-" + str2.slice(8);
return str1 + str2n;
}
// Escape null in String
function esn(val) {
if (val === null) {
return "";
} else {
return String(val);
}
}
function extractFileExtension(fileName) {
var tokens = fileName.split(".");
if (tokens.length <= 1) {
return "";
} else {
return "." + tokens[tokens.length - 1];
}
}
function generateEnvelopeXsd() {
var sb = new StringBuilder();
sb.add('<?xml version="1.0" encoding="UTF-8"?>').nl();
sb.add('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">').nl();
sb.add('<xs:element name="envelope">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="protocol" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:simpleContent>').nl();
sb.add('<xs:extension base="xs:string">').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:extension>').nl();
sb.add('</xs:simpleContent>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('<xs:attribute name="xmlns" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="version" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:schema>');
return sb.build();
}
function generateElnXsd() {
var sb = new StringBuilder();
sb.add('<?xml version="1.0" encoding="UTF-8"?>').nl();
sb.add('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">').nl();
sb.add('<xs:element name="eln">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="protocol">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="name" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="authors" type="xs:string" minOccurs="0"></xs:element>').nl();
sb.add('<xs:element name="description" type="xs:string" minOccurs="0"></xs:element>').nl();
sb.add('<xs:element name="created_at" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="updated_at" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="steps" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="step" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="name" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="description" type="xs:string" minOccurs="0"></xs:element>').nl();
sb.add('<xs:element name="checklists" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="checklist" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="name" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="items" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="item" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="text" type="xs:string"></xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="position" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('<xs:element name="assets" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="asset" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="fileName" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="fileType" type="xs:string"></xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="fileRef" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('<xs:element name="elnTables" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="elnTable" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="contents" type="xs:string"></xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="position" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="xmlns" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="version" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:schema>');
return sb.build();
}
/*********************************************/
/* ACTUAL FUNCTION CODE */
/*********************************************/
if (protocol_ids.length > 0) {
animateSpinner();
$.ajax({
type: "GET",
url: "/protocols/export",
dataType: "json",
data: { protocol_ids: protocol_ids},
success: function (data) {
var zip = new JSZip();
// Envelope code
var esb = new StringBuilder();
esb.add('<envelope xmlns="http://www.scinote.net" version="1.0">').nl();
_.each(data.protocols, function(protocol) {
var protocolGuid = getGuid(protocol.id);
// Create folder for this protocol
var protocolFolder = zip.folder(protocolGuid);
var protocolXml = [];
var psb = new StringBuilder();
// Header
psb.add('<eln xmlns="http://www.scinote.net" version="1.0">').nl();
// Protocol
psb.add('<protocol id="' + protocol.id + '" guid="' + protocolGuid + '">').nl();
psb.add('<name>' + esn(protocol.name) + '</name>').nl();
psb.add('<authors>' + esn(protocol.authors) + '</authors>').nl();
psb.add('<description>' + esn(protocol.description) + '</description>').nl();
psb.add('<created_at>' + esn(protocol.created_at) + '</created_at>').nl();
psb.add('<updated_at>' + esn(protocol.updated_at) + '</updated_at>').nl();
// Steps
if (protocol.steps.length > 0) {
psb.add('<steps>').nl();
_.each(protocol.steps, function(step) {
var stepGuid = getGuid(step.id);
var ssb = new StringBuilder();
ssb.add('<step id="' + step.id + '" guid="' + stepGuid + '" position="' + step.position + '">').nl();
ssb.add('<name>' + esn(step.name) + '</name>').nl();
ssb.add('<description>' + esn(step.description) + '</description>').nl();
// Assets
if (step.assets.length > 0) {
ssb.add('<assets>').nl();
_.each(step.assets, function(asset) {
var assetGuid = getGuid(asset.id);
// Generate the asset file inside ZIP
var assetFileName = assetGuid + extractFileExtension(asset.fileName);
var decodedData = window.atob(asset.bytes);
zip.folder(protocolGuid).folder(stepGuid).file(assetFileName, decodedData, { binary: true });
var asb = new StringBuilder();
asb.add('<asset id="' + asset.id + '" guid="' + assetGuid + '" fileRef="' + assetFileName + '">').nl();
asb.add('<fileName>' + esn(asset.fileName) + '</fileName>').nl();
asb.add('<fileType>' + esn(asset.fileType) + '</fileType>').nl();
asb.add('</asset>').nl();
ssb.add(asb.build());
});
ssb.add('</assets>').nl();
}
// Tables
if (step.tables.length > 0) {
ssb.add('<elnTables>').nl();
_.each(step.tables, function(table) {
var tsb = new StringBuilder();
tsb.add('<elnTable id="' + table.id + '" guid="' + getGuid(table.id) + '">').nl();
tsb.add('<name>' + esn(table.name) + '</name>').nl();
tsb.add('<contents>' + esn(table.contents) + '</contents>').nl();
tsb.add('</elnTable>');
ssb.add(tsb.build()).nl();
});
ssb.add('</elnTables>').nl();
}
// Checklists
if (step.checklists.length > 0) {
ssb.add('<checklists>').nl();
_.each(step.checklists, function(checklist) {
var csb = new StringBuilder();
csb.add('<checklist id="' + checklist.id + '" guid="' + getGuid(checklist.id) + '">').nl();
csb.add('<name>' + esn(checklist.name) + '</name>').nl();
if (checklist.items.length > 0) {
csb.add('<items>').nl();
_.each(checklist.items, function(item) {
var isb = new StringBuilder();
isb.add('<item id="' + item.id + '" guid="' + getGuid(item.id) + '" position="' + item.position + '">').nl();
isb.add('<text>' + esn(item.text) + '</text>').nl();
isb.add('</item>');
csb.add(isb.build()).nl();
});
csb.add('</items>').nl();
}
csb.add('</checklist>');
ssb.add(csb.build()).nl();
});
ssb.add('</checklists>').nl();
}
psb.add(ssb.build());
psb.add('</step>').nl();
});
psb.add('</steps>').nl();
}
psb.add('</protocol>').nl();
psb.add('</eln>');
// Okay, we have a generated XML for
// individual protocol, save it to protocol folder
protocolFolder.file("eln.xml", psb.build());
// Add protocol to the envelope
esb.add('<protocol id="' + protocol.id + '" guid="' + protocolGuid + '">' + esn(protocol.name) + '</protocol>').nl();
});
esb.add('</envelope>').nl();
// Save envelope to root directory in ZIP
zip.file("scinote.xml", esb.build());
// Add the XSD schemes into the ZIP
zip.file("scinote.xsd", generateEnvelopeXsd());
zip.file("eln.xsd", generateElnXsd());
// NOW, DOWNLOAD THE ZIP
var blob = zip.generate({ type: "blob" });
var fileName = "protocol.eln";
if (data.protocols.length === 1) {
// Try to construct an OS-safe file name
if (data.protocols[0].name.length && data.protocols[0].name.length > 0) {
var escapedName = data.protocols[0].name.replace(/[^0-9a-zA-Z-.,_]/gi, '_').toLowerCase().substring(0, 250);
if (escapedName.length > 0) {
fileName = escapedName + ".eln";
}
}
} else if (data.protocols.length > 1) {
fileName = "protocols.eln";
}
var link = document.createElement("a");
if (link.download !== undefined) {
// Browsers that support HTML5 download attribute
link.setAttribute("href", window.URL.createObjectURL(blob));
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
alert("Please use latest version of Chrome, Firefox, or Opera browser for the export!");
}
animateSpinner(null, false);
},
error: function() {
alert("Ajax error!");
}
});
}
}

View file

@ -1,5 +1,4 @@
//= require protocols/import_export/import
//= require protocols/import_export/export
//= require datatables
// Global variables
@ -547,6 +546,18 @@ function updateButtons() {
}
}
function exportProtocols(ids) {
if (ids.length > 0) {
var params = '?protocol_ids[]=' + ids[0];
for (var i = 1; i < ids.length; i++) {
params += '&protocol_ids[]=' + ids[i];
}
params = encodeURI(params);
window.location.href = $("[data-action='export']")
.data('export-url') + params;
}
}
function editSelectedProtocol() {
if (rowsSelected.length && rowsSelected.length == 1) {
var row = $("tr[data-row-id='" + rowsSelected[0] + "']");

View file

@ -560,12 +560,58 @@ class ProtocolsController < ApplicationController
end
def export
# Make a zip output stream and send it to the client
respond_to do |format|
format.json {
render json: {
protocols: export_protocols(@protocols)
}, status: :ok
}
format.html do
z_output_stream = Zip::OutputStream.write_buffer do |ostream|
ostream.put_next_entry('scinote.xml')
ostream.print(generate_envelope_xml(@protocols))
ostream.put_next_entry('scinote.xsd')
ostream.print(generate_envelope_xsd)
ostream.put_next_entry('eln.xsd')
ostream.print(generate_eln_xsd)
# Create folder and xml file for each protocol and populate it
@protocols.each do |protocol|
protocol_dir = get_guid(protocol.id).to_s
ostream.put_next_entry("#{protocol_dir}/eln.xml")
ostream.print(generate_protocol_xml(protocol))
# Add assets to protocol folder
next if protocol.steps.count <= 0
protocol.steps.order(:id).each do |step|
step_guid = get_guid(step.id)
step_dir = "#{protocol_dir}/#{step_guid}"
next if step.assets.count <= 0
step.assets.order(:id).each do |asset|
asset_guid = get_guid(asset.id)
asset_file_name = asset_guid.to_s +
File.extname(asset.file_file_name).to_s
ostream.put_next_entry("#{step_dir}/#{asset_file_name}")
input_file = asset.open
ostream.print(input_file.read)
input_file.close
end
end
end
end
z_output_stream.rewind
protocol_name = get_protocol_name(@protocols[0])
# Now generate filename of the archive and send file to user
if @protocols.count == 1
# Try to construct an OS-safe file name
file_name = 'protocol.eln'
unless protocol_name.nil?
escaped_name = protocol_name.gsub(/[^0-9a-zA-Z\-.,_]/i, '_')
.downcase[0..Constants::NAME_MAX_LENGTH]
file_name = escaped_name + '.eln' unless escaped_name.empty?
end
elsif @protocols.length > 1
file_name = 'protocols.eln'
end
send_data(z_output_stream.read, filename: file_name)
end
end
end

View file

@ -1,89 +1,288 @@
require 'zip'
module ProtocolsExporter
def export_protocols(protocols)
protocols_json = []
protocols.each do |protocol|
protocols_json << export_protocol(protocol)
end
return protocols_json
end
private
def export_protocol(protocol)
protocol_json = protocol.as_json(only: [
:id,
:name,
:description,
:authors,
:created_at,
:updated_at
])
def get_guid(id)
str1 = '00000000-0000-'
str2 = id.to_s
str2 = '0' * (19 - str2.size) + str2
str2 = '4' + str2
str2n = str2[0..3] + '-' + str2[4..7] + '-' + str2[8..-1]
str1 + str2n
end
# "Inject" module's name
def get_protocol_name(protocol)
## "Inject" module's name
if protocol.in_module? && protocol.name.blank?
protocol_json["name"] = protocol.my_module.name
protocol_name = protocol.my_module.name
else
protocol_name = protocol.name
end
protocol_name
end
protocol_json["steps"] = []
protocol.steps.find_each do |step|
step_json = step.as_json(only: [
:id,
:name,
:description,
:position
])
def generate_envelope_xml(protocols)
envelope_xml = "<envelope xmlns=\"http://www.scinote.net\" " \
"version=\"1.0\">\n"
protocols.each do |protocol|
protocol_name = get_protocol_name(protocol)
envelope_xml << "<protocol id=\"#{protocol.id}\" " \
"guid=\"#{get_guid(protocol.id)}\">#{protocol_name}" \
"</protocol>\n"
end
envelope_xml << "</envelope>\n"
envelope_xml
end
step_json["tables"] = []
step.tables.find_each do |table|
table_json = table.as_json(only: [:id, :name])
table_json["contents"] = table.contents.unpack("H*")[0]
def generate_protocol_xml(protocol)
protocol_name = get_protocol_name(protocol)
protocol_xml = "<eln xmlns=\"http://www.scinote.net\" version=\"1.0\">\n"
protocol_xml << "<protocol id=\"#{protocol.id}\" " \
"guid=\"#{get_guid(protocol.id)}\">\n"
protocol_xml << "<name>#{protocol_name}</name>\n"
protocol_xml << "<authors>#{protocol.authors}</authors>\n"
protocol_xml << "<description>#{protocol.description}</description>\n"
protocol_xml << "<created_at>#{protocol.created_at.as_json}</created_at>\n"
protocol_xml << "<updated_at>#{protocol.updated_at.as_json}</updated_at>\n"
step_json["tables"] << table_json
end
# Steps
if protocol.steps.count > 0
protocol_xml << "<steps>\n"
protocol.steps.order(:id).each do |step|
step_guid = get_guid(step.id)
step_xml = "<step id=\"#{step.id}\" guid=\"#{step_guid}\" " \
"position=\"#{step.position}\">\n"
step_xml << "<name>#{step.name}</name>\n"
step_xml << "<description>#{step.description}</description>\n"
step_json["assets"] = []
step.assets.find_each do |asset|
asset_json = asset.as_json(only: [
:id,
:file_file_name,
:file_content_type
])
asset_json["fileName"] = asset_json.delete("file_file_name")
asset_json["fileType"] = asset_json.delete("file_content_type")
# Retrieve file contents
file = asset.open
asset_json["bytes"] = Base64.encode64(file.read)
file.close
step_json["assets"] << asset_json
end
step_json["checklists"] = []
step.checklists.find_each do |checklist|
checklist_json = checklist.as_json(only: [
:id,
:name
])
checklist_json["items"] = []
checklist.checklist_items.find_each do |item|
item_json = item.as_json(only: [
:id,
:text,
:position
])
checklist_json["items"] << item_json
# Assets
if step.assets.count > 0
step_xml << "<assets>\n"
step.assets.order(:id).each do |asset|
asset_guid = get_guid(asset.id)
asset_file_name = "#{asset_guid}" \
"#{File.extname(asset.file_file_name)}"
asset_xml = "<asset id=\"#{asset.id}\" guid=\"#{asset_guid}\" " \
"fileRef=\"#{asset_file_name}\">\n"
asset_xml << "<fileName>#{asset.file_file_name}</fileName>\n"
asset_xml << "<fileType>#{asset.file_content_type}</fileType>\n"
asset_xml << "</asset>\n"
step_xml << asset_xml
end
step_xml << "</assets>\n"
end
step_json["checklists"] << checklist_json
# Tables
if step.tables.count > 0
step_xml << "<elnTables>\n"
step.tables.order(:id).each do |table|
table_xml = "<elnTable id=\"#{table.id}\" " \
"guid=\"#{get_guid(table.id)}\">\n"
table_xml << "<contents>#{table.contents.unpack('H*')[0]}" \
"</contents>\n"
table_xml << "</elnTable>\n"
step_xml << table_xml
end
step_xml << "</elnTables>\n"
end
# Checklists
if step.checklists.count > 0
step_xml << "<checklists>\n"
step.checklists.order(:id).each do |checklist|
checklist_xml = "<checklist id=\"#{checklist.id}\" " \
"guid=\"#{get_guid(checklist.id)}\">\n"
checklist_xml << "<name>#{checklist.name}</name>\n"
if checklist.items
checklist_xml << "<items>\n"
checklist.items.each do |item|
item_xml = "<item id=\"#{item.id}\" " \
"guid=\"#{get_guid(item.id)}\" " \
"position=\"#{item.position}\">\n"
item_xml << "<text>#{item.text}</text>\n"
item_xml << "</item>\n"
checklist_xml << item_xml
end
checklist_xml << "</items>\n"
end
checklist_xml << "</checklist>\n"
step_xml << checklist_xml
end
step_xml << "</checklists>\n"
end
protocol_xml << step_xml
protocol_xml << "</step>\n"
end
protocol_json["steps"] << step_json
protocol_xml << "</steps>\n"
end
return protocol_json
protocol_xml << "</protocol>\n"
protocol_xml << '</eln>'
protocol_xml
end
end
def generate_envelope_xsd
envelope_xsd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
envelope_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" \
" elementFormDefault=\"qualified\" " \
"attributeFormDefault=\"unqualified\">\n"
envelope_xsd << "<xs:element name=\"envelope\">\n"
envelope_xsd << "<xs:complexType>\n"
envelope_xsd << "<xs:sequence>\n"
envelope_xsd << "<xs:element name=\"protocol\" maxOccurs=\"unbounded\">\n"
envelope_xsd << "<xs:complexType>\n"
envelope_xsd << "<xs:simpleContent>\n"
envelope_xsd << "<xs:extension base=\"xs:string\">\n"
envelope_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "</xs:extension>\n"
envelope_xsd << "</xs:simpleContent>\n"
envelope_xsd << "</xs:complexType>\n"
envelope_xsd << "</xs:element>\n"
envelope_xsd << "</xs:sequence>\n"
envelope_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "</xs:complexType>\n"
envelope_xsd << "</xs:element>\n"
envelope_xsd << "</xs:schema>\n"
envelope_xsd
end
def generate_eln_xsd
eln_xsd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
eln_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" " \
"elementFormDefault=\"qualified\" " \
"attributeFormDefault=\"unqualified\">\n"
eln_xsd << "<xs:element name=\"eln\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"protocol\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"authors\" type=\"xs:string\" " \
"minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" " \
"minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"created_at\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"updated_at\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"steps\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
eln_xsd << "<xs:element name=\"step\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" " \
"minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"checklists\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
eln_xsd << "<xs:element name=\"checklist\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"items\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
eln_xsd << "<xs:element name=\"item\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"text\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\">" \
"</xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "<xs:element name=\"assets\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
eln_xsd << "<xs:element name=\"asset\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"fileName\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"fileType\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"fileRef\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "<xs:element name=\"elnTables\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
eln_xsd << "<xs:element name=\"elnTable\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"contents\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:schema>\n"
end
end

View file

@ -24,7 +24,7 @@
</ul>
</div>
<% if can_export_protocol_from_module(@my_module) %>
<a href="#" class="btn btn-default" data-action="export" data-id="<%= @protocol.id %>"><span class="glyphicon glyphicon-export"></span><span class="hidden-xs-custom">&nbsp;<%= t("my_modules.protocols.buttons.export") %></span></a>
<%= link_to raw("<span class=\"glyphicon glyphicon-export\"></span><span class=\"hidden-xs-custom\">&nbsp;" + t('my_modules.protocols.buttons.export') + "</span>"), export_protocols_path(protocol_ids: @protocol.id), class: "btn btn-default", data: {no_turbolink: true} %>
<% else %>
<a href="#" class="btn btn-default disabled"><span class="glyphicon glyphicon-export"></span><span class="hidden-xs-custom">&nbsp;<%= t("my_modules.protocols.buttons.export") %></span></a>
<% end %>

View file

@ -58,7 +58,7 @@
data-type="<%= @type %>" data-import-url="<%= import_protocols_path %>"
<%= 'disabled="disabled"' unless can_import_protocols(@current_organization) %>>
</a>
<a class="btn btn-default" data-action="export">
<a class="btn btn-default" data-action="export" data-export-url="<%= export_protocols_path() %>">
<span class="glyphicon glyphicon-export"></span>
<span class="hidden-xs">&nbsp;<%= t("protocols.index.export") %></span>
</a>