Implement V2 protocols exporter [SCI-6876] (#4142)

* Implement V2 protocols exporter [SCI-6876]

* Implement new importer [SCI-6877]
This commit is contained in:
artoscinote 2022-06-10 10:18:55 +02:00 committed by GitHub
parent ada26d8036
commit 5c051ef8c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 633 additions and 125 deletions

View file

@ -73,4 +73,4 @@ function generateElnTable(id, content) {
}
var html = html_s + tr + html_e;
return $.parseHTML(html);
}
}

View file

@ -80,6 +80,10 @@ function importProtocolFromFile(
element.find("[data-toggle='" + name + "']").hide();
}
function showPartOfElement(element, name) {
element.find("[data-toggle='" + name + "']").show();
}
function newAssetElement(folder, stepGuid, fileRef, fileName, fileType) {
var html = '<li>';
var assetBytes;
@ -214,20 +218,7 @@ function importProtocolFromFile(
tableNodes = node.find('elnTables > elnTable');
if (tableNodes.length > 0) {
tableNodes.each(function() {
var tableId = $(this).attr('id');
var tableName = $(this).children('name').text();
var tableContent = $(this).children('contents').text();
// Generate table element
var tableEl = newPreviewElement(
'table',
{ name: tableName }
);
var elnTableEl = generateElnTable(tableId, tableContent);
addChildToPreviewElement(tableEl, 'table', elnTableEl);
// Now, append table element to step
addChildToPreviewElement(stepEl, 'tables', tableEl);
addTablePreview(stepEl, this);
});
} else {
hidePartOfElement(stepEl, 'tables');
@ -237,41 +228,99 @@ function importProtocolFromFile(
checklistNodes = node.find('checklists > checklist');
if (checklistNodes.length > 0) {
checklistNodes.each(function() {
var checklistId = $(this).attr('id');
var checklistName = $(this).children('name').text();
var checklistEl = newPreviewElement(
'checklist',
{ name: checklistName }
);
// Iterate through checklist items
$(this).find('items > item').each(function() {
var itemId = $(this).attr('id');
var itemText = $(this).children('text').text();
var itemEl = newPreviewElement(
'checklist-item',
{ text: itemText }
);
addChildToPreviewElement(checklistEl, 'checklist-items', itemEl);
});
// Now, add checklist item to step
addChildToPreviewElement(stepEl, 'checklists', checklistEl);
addChecklistPreview(stepEl, this);
});
}
// Parse step elements
$(this).find('stepElements > stepElement').each(function() {
$element = $(this);
switch ($(this).attr('type')) {
case 'Checklist':
addChecklistPreview(stepEl, $(this).find('checklist'));
showPartOfElement(stepEl, 'checklists');
break;
case 'StepTable':
addTablePreview(stepEl, $(this).find('elnTable'));
showPartOfElement(stepEl, 'tables');
break;
case 'StepText':
addStepTextPreview(stepEl, $(this).find('stepText'), protocolFolders[position], stepGuid);
showPartOfElement(stepEl, 'step-texts');
break;
default:
// nothing to do
break;
}
});
// Append step element to preview container
previewContainer.append(stepEl);
});
}
function addTablePreview(stepEl, tableNode) {
var tableId = $(tableNode).attr('id');
var tableName = $(tableNode).children('name').text();
var tableContent = $(tableNode).children('contents').text();
// Generate table element
var tableEl = newPreviewElement(
'table',
{ name: tableName }
);
var elnTableEl = generateElnTable(tableId, tableContent);
addChildToPreviewElement(tableEl, 'table', elnTableEl);
// Now, append table element to step
addChildToPreviewElement(stepEl, 'tables', tableEl);
}
function addChecklistPreview(stepEl, checklistNode) {
var checklistId = $(checklistNode).attr('id');
var checklistName = $(checklistNode).children('name').text();
var checklistEl = newPreviewElement(
'checklist',
{ name: checklistName }
);
// Iterate through checklist items
$(checklistNode).find('items > item').each(function() {
var itemId = $(this).attr('id');
var itemText = $(this).children('text').text();
var itemEl = newPreviewElement(
'checklist-item',
{ text: itemText }
);
addChildToPreviewElement(checklistEl, 'checklist-items', itemEl);
});
// Now, add checklist item to step
addChildToPreviewElement(stepEl, 'checklists', checklistEl);
}
function addStepTextPreview(stepEl, stepTextNode, folder, stepGuid) {
var itemId = $(stepTextNode).attr('id');
var itemText = displayTinyMceAssetInDescription(stepTextNode, folder, stepGuid);
var textEl = newPreviewElement(
'step-text',
{ text: itemText }
);
addChildToPreviewElement(stepEl, 'step-texts', textEl);
}
// display tiny_mce_assets in step description
function displayTinyMceAssetInDescription(node, folder, stepGuid) {
var description;
var description = node.children('description').html() || node.children('contents').html();
if (!description) return '';
if (node.children('descriptionAssets').length === 0) {
description = node.children('description').html();
return $('<div></div>').html(
description.replace(/\[~tiny_mce_id:([0-9a-zA-Z]+)\]/,
'<strong>Can\'t import image</strong>')
@ -281,7 +330,7 @@ function importProtocolFromFile(
.replace(' ]]&gt;', '')
).html();
}
description = node.children('description').html();
description = description.replace('<!--[CDATA[ ', '')
.replace(' ]]--&gt;', '')
.replace(' ]]-->', '')
@ -518,6 +567,76 @@ function importProtocolFromFile(
return result;
}
function stepTextJson(stepTextNode, folderIndex, stepGuid) {
var json = {};
json.contents = $('<div></div>').html(
stepTextNode.children('contents')
.html()
.replace('<!--[CDATA[', '')
.replace(' ]]-->', '')
.replace(']]&gt;', '')
).html();
json.descriptionAssets = prepareTinyMceAssets(stepTextNode, folderIndex, stepGuid);
return json;
}
function stepTableJson(tableNode) {
var json = {};
var contents = tableNode.children('contents').text();
json.id = tableNode.attr('id');
json.name = tableNode.children('name').text();
contents = hex2a(contents);
contents = window.btoa(contents);
json.contents = contents;
return json;
}
function checklistJson(checklistNode) {
var json = {};
var itemsJson = [];
json.id = checklistNode.attr('id');
json.name = checklistNode.children('name').text();
// Iterate through checklist items
checklistNode.find('items > item').each(function() {
var itemJson = {};
itemJson.id = $(this).attr('id');
itemJson.position = $(this).attr('position');
itemJson.text = $(this).children('text').text();
itemsJson.push(itemJson);
});
json.items = itemsJson;
return json;
}
function stepElementJson(stepElementNode, folderIndex, stepGuid) {
var json = {
type: stepElementNode.attr('type')
};
switch (json.type) {
case 'Checklist':
json.checklist = checklistJson(stepElementNode.find('checklist'));
break;
case 'StepTable':
json.elnTable = stepTableJson(stepElementNode.find('elnTable'));
break;
case 'StepText':
json.stepText = stepTextJson(stepElementNode.find('stepText'), folderIndex, stepGuid);
break;
default:
// nothing to do
break;
}
return json;
}
function importSingleProtocol(index, replaceVals, resultCallback) {
// Retrieve general protocol info
var name;
@ -551,6 +670,7 @@ function importProtocolFromFile(
// Allright, let's construct the huge,
// messy-pot of a protocol JSON
protocolJson.elnVersion = $(protocolXmls[index]).attr('version');
protocolJson.name = name;
protocolJson.description = protocolDescription;
protocolJson.authors = authors;
@ -571,14 +691,16 @@ function importProtocolFromFile(
stepJson.position = $(this).attr('position');
stepJson.name = $(this).children('name').text();
// I know is crazy but is the only way I found to pass valid HTML
stepJson.description = $('<div></div>').html($(this)
.children('description')
.html()
.replace('<!--[CDATA[', '')
.replace(' ]]-->', '')
.replace(']]&gt;', ''))
.html();
// Iterage throug tiny_mce_assets
if ($(this).children('description').html()) {
stepJson.description = $('<div></div>').html($(this)
.children('description')
.html()
.replace('<!--[CDATA[', '')
.replace(' ]]-->', '')
.replace(']]&gt;', ''))
.html();
}
// Iterate through tiny_mce_assets
stepJson.descriptionAssets = prepareTinyMceAssets(this, index, stepGuid);
// Iterate through assets
@ -608,39 +730,23 @@ function importProtocolFromFile(
// Iterate through step tables
$(this).find('elnTables > elnTable').each(function() {
var stepTableJson = {};
var contents = $(this).children('contents').text();
stepTableJson.id = $(this).attr('id');
stepTableJson.name = $(this).children('name').text();
contents = hex2a(contents);
contents = window.btoa(contents);
stepTableJson.contents = contents;
stepTablesJson.push(stepTableJson);
stepTablesJson.push(stepTableJson($(this)));
});
stepJson.tables = stepTablesJson;
// Iterate through checklists
$(this).find('checklists > checklist').each(function() {
var stepChecklistJson = {};
var itemsJson = [];
stepChecklistJson.id = $(this).attr('id');
stepChecklistJson.name = $(this).children('name').text();
// Iterate through checklist items
$(this).find('items > item').each(function() {
var itemJson = {};
itemJson.id = $(this).attr('id');
itemJson.position = $(this).attr('position');
itemJson.text = $(this).children('text').text();
itemsJson.push(itemJson);
});
stepChecklistJson.items = itemsJson;
stepChecklistsJson.push(stepChecklistJson);
stepChecklistsJson.push(checklistJson($(this)));
});
stepJson.checklists = stepChecklistsJson;
// Parse step elements
stepJson.stepElements = [];
$(this).find('stepElements > stepElement').each(function() {
stepJson.stepElements.push(stepElementJson($(this), index, stepGuid));
});
stepsJson.push(stepJson);
});
protocolJson.steps = stepsJson;

View file

@ -1,8 +1,6 @@
class ProtocolsController < ApplicationController
include RenamingUtil
include ProtocolsImporter
include ProtocolsExporter
include ActionView::Helpers::TextHelper
include ActionView::Helpers::UrlHelper
include ApplicationHelper
@ -10,6 +8,7 @@ class ProtocolsController < ApplicationController
include ProtocolsIoHelper
include TeamsHelper
include CommentHelper
include ProtocolsExporterV2
before_action :check_create_permissions, only: %i(
create
@ -74,6 +73,8 @@ class ProtocolsController < ApplicationController
before_action :check_protocolsio_import_permissions,
only: %i(protocolsio_import_create protocolsio_import_save)
before_action :set_importer, only: %i(load_from_file import)
def index; end
def datatable
@ -524,14 +525,14 @@ class ProtocolsController < ApplicationController
if @protocol.can_destroy?
transaction_error = false
Protocol.transaction do
begin
import_into_existing(
@protocol, @protocol_json, current_user, current_team
)
rescue Exception
transaction_error = true
raise ActiveRecord:: Rollback
end
@importer.import_into_existing(
@protocol, @protocol_json
)
rescue StandardError => e
transaction_error = true
Rails.logger.error(e.message)
Rails.logger.error(e.backtrace.join("\n"))
raise ActiveRecord::Rollback
end
if transaction_error
@ -564,14 +565,12 @@ class ProtocolsController < ApplicationController
respond_to do |format|
transaction_error = false
Protocol.transaction do
begin
protocol =
import_new_protocol(@protocol_json, @team, @type, current_user)
rescue StandardError => ex
Rails.logger.error ex.message
transaction_error = true
raise ActiveRecord:: Rollback
end
protocol =
@importer.import_new_protocol(@protocol_json, @type)
rescue StandardError => e
Rails.logger.error e.backtrace.join("\n")
transaction_error = true
raise ActiveRecord::Rollback
end
p_name =
@ -659,7 +658,7 @@ class ProtocolsController < ApplicationController
# and some modals get closed and opened
end
rescue StandardError => e
Rails.logger.error(e.message)
Rails.logger.error(e.backtrace.join("\n"))
@protocolsio_general_error = true
respond_to do |format|
format.js {}
@ -697,8 +696,8 @@ class ProtocolsController < ApplicationController
@protocolsio_general_error = false
Protocol.transaction do
begin
protocol = import_new_protocol(
@db_json, current_team, params[:type].to_sym, current_user
protocol = @importer.import_new_protocol(
@db_json, params[:type].to_sym
)
rescue Exception
transaction_error = true
@ -763,6 +762,10 @@ class ProtocolsController < ApplicationController
end
end
ostream = step.tiny_mce_assets.save_to_eln(ostream, step_dir)
step.step_texts.each do |step_text|
ostream = step_text.tiny_mce_assets.save_to_eln(ostream, step_dir)
end
end
end
end
@ -989,6 +992,15 @@ class ProtocolsController < ApplicationController
private
def set_importer
case params.dig('protocol', 'elnVersion')
when '1.0'
@importer = ProtocolsImporter.new(current_user, current_team)
when '1.1'
@importer = ProtocolsImporterV2.new(current_user, current_team)
end
end
def valid_protocol_json(json)
JSON.parse(json)
return true

View file

@ -43,6 +43,7 @@
<li>
<a data-action="load-from-file"
class="btn-open-file"
:data-import-url="protocol.attributes.urls.import_url"
:class="{ disabled: !protocol.attributes.urls.import_url }">
<span class="fas fa-download"></span>
<span>{{ i18n.t("my_modules.protocol.options_dropdown.import") }}</span>

View file

@ -0,0 +1,136 @@
require 'zip'
module ProtocolsExporterV2
include ProtocolsExporter
private
def generate_envelope_xml(protocols)
envelope_xml = "<envelope xmlns=\"http://www.scinote.net\" " \
"version=\"1.1\">\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
def generate_protocol_xml(protocol)
protocol_name = get_protocol_name(protocol)
protocol_xml =
"<eln xmlns=\"http://www.scinote.net\" version=\"1.1\">\n" \
"<protocol id=\"#{protocol.id}\" guid=\"#{get_guid(protocol.id)}\">\n" \
"<name>#{protocol_name}</name>\n" \
"<authors>#{protocol.authors}</authors>\n" \
"<description>" \
"<!--[CDATA[ #{Nokogiri::HTML::DocumentFragment.parse(protocol.description)} ]]-->" \
"</description>\n"
if tiny_mce_asset_present?(protocol) && protocol.description
protocol_xml << get_tiny_mce_assets(protocol.description)
end
protocol_xml << "<created_at>#{protocol.created_at.as_json}</created_at>\n"
protocol_xml << "<updated_at>#{protocol.updated_at.as_json}</updated_at>\n"
# Steps
if protocol.steps.any?
protocol_xml <<
"<steps>\n" \
"#{protocol.steps.order(:position).map { |s| step_xml(s) }.join}" \
"</steps>\n"
end
protocol_xml << "</protocol>\n"
protocol_xml << '</eln>'
protocol_xml
end
def step_xml(step)
step_guid = get_guid(step.id)
xml = "<step id=\"#{step.id}\" guid=\"#{step_guid}\" position=\"#{step.position}\">\n" \
"<name>#{step.name}</name>\n"
# Assets
xml << "<assets>\n#{step.assets.map { |a| asset_xml(a) }.join}</assets>\n" if step.assets.any?
if step.step_orderable_elements.any?
xml << "<stepElements>\n"
step.step_orderable_elements.find_each do |step_orderable_element|
element = step_orderable_element.orderable
element_guid = get_guid(element.id)
xml << "<stepElement type=\"#{step_orderable_element.orderable_type}\" guid=\"#{element_guid}\" " \
"position=\"#{step_orderable_element.position}\">"
case element
when StepText
xml << step_text_xml(element)
when StepTable
xml << table_xml(element.table)
when Checklist
xml << checklist_xml(element)
end
xml << "</stepElement>\n"
end
xml << "</stepElements>\n"
end
xml << '</step>'
xml
end
def step_text_xml(step_text)
xml = "<stepText id=\"#{step_text.id}\" guid=\"#{get_guid(step_text.id)}\">\n" \
"<contents>\n" \
"<!--[CDATA[ #{Nokogiri::HTML::DocumentFragment.parse(step_text.text)} ]]-->"\
"</contents>\n"
xml << get_tiny_mce_assets(step_text.text)
xml << "</stepText>\n"
end
def table_xml(table)
"<elnTable id=\"#{table.id}\" guid=\"#{get_guid(table.id)}\">\n" \
"<name>#{table.name}</name>\n" \
"<contents>#{table.contents.unpack1('H*')}</contents>\n" \
"</elnTable>\n"
end
def checklist_xml(checklist)
xml = "<checklist id=\"#{checklist.id}\" guid=\"#{get_guid(checklist.id)}\">\n" \
"<name>#{checklist.name}</name>\n"
if checklist.checklist_items.any?
xml << "<items>\n#{checklist.checklist_items.map { |ci| checklist_item_xml(ci) }.join}</items>\n"
end
xml << "</checklist>\n"
xml
end
def checklist_item_xml(checklist_item)
"<item id=\"#{checklist_item.id}\" " \
"guid=\"#{get_guid(checklist_item.id)}\" " \
"position=\"#{checklist_item.position}\">\n" \
"<text>#{checklist_item.text}</text>\n" \
"</item>\n" \
end
def asset_xml(asset)
asset_guid = get_guid(asset.id)
"#{asset_guid}#{File.extname(asset.file_name)}" \
"<asset id=\"#{asset.id}\" guid=\"#{asset_guid}\" fileRef=\"#{asset_file_name}\">\n" \
"<fileName>#{asset.file_name}</fileName>\n" \
"<fileType>#{asset.content_type}</fileType>\n" \
"<fileMetadata><!--[CDATA[ #{asset.file.metadata.to_json} ]]--></fileMetadata>\n" \
"</asset>\n"
end
end

View file

@ -1,17 +1,22 @@
# frozen_string_literal: true
module ProtocolsImporter
class ProtocolsImporter
include RenamingUtil
def import_new_protocol(protocol_json, team, type, user)
def initialize(user, team)
@user = user
@team = team
end
def import_new_protocol(protocol_json, type)
remove_empty_inputs(protocol_json)
protocol = Protocol.new(
name: protocol_json['name'],
authors: protocol_json['authors'],
protocol_type: (type == :public ? :in_repository_public : :in_repository_private),
published_on: (type == :public ? Time.now : nil),
added_by: user,
team: team
added_by: @user,
team: @team
)
# Try to rename record
@ -21,19 +26,19 @@ module ProtocolsImporter
protocol.save!
# Protocol is saved, populate it
populate_protocol(protocol, protocol_json, user, team)
populate_protocol(protocol, protocol_json)
protocol
end
def import_into_existing(protocol, protocol_json, user, team)
def import_into_existing(protocol, protocol_json)
# Firstly, destroy existing protocol's contents
protocol.tiny_mce_assets.destroy_all
protocol.destroy_contents
protocol.reload
# Alright, now populate the protocol
populate_protocol(protocol, protocol_json, user, team)
populate_protocol(protocol, protocol_json)
protocol.reload
# Unlink the protocol
@ -43,9 +48,20 @@ module ProtocolsImporter
private
def populate_protocol(protocol, protocol_json, user, team)
def create_in_step!(step, new_orderable)
ActiveRecord::Base.transaction do
new_orderable.save!
step.step_orderable_elements.create!(
position: step.step_orderable_elements.length,
orderable: new_orderable
)
end
end
def populate_protocol(protocol, protocol_json)
protocol.reload
protocol.description = populate_rte(protocol_json, protocol, team)
protocol.description = populate_rte(protocol_json, protocol)
protocol.save!
asset_ids = []
step_pos = 0
@ -55,22 +71,27 @@ module ProtocolsImporter
name: step_json['name'],
position: step_pos,
completed: false,
user: user,
last_modified_by: user,
user: @user,
last_modified_by: @user,
protocol: protocol
)
# need step id to link image to step
step.description = populate_rte(step_json, step, team)
step_description_text = step.step_texts.build(text: populate_rte(step_json, step))
create_in_step!(step, step_description_text)
step.save!
step_pos += 1
step_json['checklists']&.values&.each do |checklist_json|
checklist = Checklist.create!(
checklist = Checklist.new(
name: checklist_json['name'],
step: step,
created_by: user,
last_modified_by: user
created_by: @user,
last_modified_by: @user
)
create_in_step!(step, checklist)
next unless checklist_json['items']
item_pos = 0
@ -79,8 +100,8 @@ module ProtocolsImporter
text: item_json['text'],
checked: false,
position: item_pos,
created_by: user,
last_modified_by: user,
created_by: @user,
last_modified_by: @user,
checklist: checklist
)
item_pos += 1
@ -88,26 +109,27 @@ module ProtocolsImporter
end
step_json['tables']&.values&.each do |table_json|
table = Table.create!(
name: table_json['name'],
contents: Base64.decode64(table_json['contents']),
created_by: user,
last_modified_by: user,
team: team
)
StepTable.create!(
step_table = StepTable.new(
step: step,
table: table
table: Table.new(
name: table_json['name'],
contents: Base64.decode64(table_json['contents']),
created_by: @user,
last_modified_by: @user,
team: @team
)
)
create_in_step!(step, step_table)
end
next unless step_json['assets']
step_json['assets']&.values&.each do |asset_json|
asset = Asset.new(
created_by: user,
last_modified_by: user,
team: team
created_by: @user,
last_modified_by: @user,
team: @team
)
# Decode the file bytes
@ -143,14 +165,14 @@ module ProtocolsImporter
end
# create tiny_mce assets and change the inport tokens
def populate_rte(object_json, object, team)
def populate_rte(object_json, object)
return populate_rte_legacy(object_json) unless object_json['descriptionAssets']
description = TinyMceAsset.update_old_tinymce(object_json['description'], nil, true)
object_json['descriptionAssets'].values.each do |tiny_mce_img_json|
tiny_mce_img = TinyMceAsset.new(
object: object,
team_id: team.id,
team_id: @team.id,
saved: true
)
tiny_mce_img.save!

View file

@ -0,0 +1,224 @@
# frozen_string_literal: true
class ProtocolsImporterV2
include RenamingUtil
def initialize(user, team)
@user = user
@team = team
end
def import_new_protocol(protocol_json, type)
remove_empty_inputs(protocol_json)
protocol = Protocol.new(
name: protocol_json['name'],
authors: protocol_json['authors'],
protocol_type: (type == :public ? :in_repository_public : :in_repository_private),
published_on: (type == :public ? Time.now : nil),
added_by: @user,
team: @team
)
# Try to rename record
rename_record(protocol, :name) if protocol.invalid?
# Okay, now save the protocol
protocol.save!
# Protocol is saved, populate it
populate_protocol(protocol, protocol_json)
protocol
end
def import_into_existing(protocol, protocol_json)
# Firstly, destroy existing protocol's contents
protocol.tiny_mce_assets.destroy_all
protocol.destroy_contents
protocol.reload
# Alright, now populate the protocol
populate_protocol(protocol, protocol_json)
protocol.reload
# Unlink the protocol
protocol.unlink
protocol
end
private
def create_in_step!(step, new_orderable)
ActiveRecord::Base.transaction do
new_orderable.save!
step.step_orderable_elements.create!(
position: step.step_orderable_elements.length,
orderable: new_orderable
)
end
end
def populate_protocol(protocol, protocol_json)
protocol.reload
protocol.description = populate_rte(protocol_json, protocol)
protocol.save!
asset_ids = []
step_pos = 0
# Check if protocol has steps
protocol_json['steps']&.values&.each do |step_json|
step = Step.create!(
name: step_json['name'],
position: step_pos,
completed: false,
user: @user,
last_modified_by: @user,
protocol: protocol
)
step.save!
step_pos += 1
step_json['stepElements']&.values&.each do |element_params|
case element_params['type']
when 'StepText'
create_step_text(step, element_params['stepText'])
when 'StepTable'
create_step_table(step, element_params['elnTable'])
when 'Checklist'
create_checklist(step, element_params['checklist'])
end
end
next unless step_json['assets']
create_assets(step_json)
end
# Post process assets
asset_ids.each do |asset_id|
Asset.find(asset_id).post_process_file(protocol.team)
end
end
def create_assets(step_json)
step_json['assets']&.values&.each do |asset_json|
asset = Asset.new(
created_by: @user,
last_modified_by: @user,
team: @team
)
# Decode the file bytes
asset.file.attach(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
filename: asset_json['fileName'],
content_type: asset_json['fileType'],
metadata: JSON.parse(asset_json['fileMetadata'] || '{}'))
asset.save!
asset_ids << asset.id
StepAsset.create!(
step: step,
asset: asset
)
end
end
def create_step_text(step, params)
step_text = StepText.create!(
step: step
)
step_text.update!(text: populate_rte(params, step_text))
create_in_step!(step, step_text)
end
def create_step_table(step, params)
step_table = StepTable.new(
step: step,
table: Table.new(
name: params['name'],
contents: Base64.decode64(params['contents']),
created_by: @user,
last_modified_by: @user,
team: @team
)
)
create_in_step!(step, step_table)
end
def create_checklist(step, params)
checklist = Checklist.new(
name: params['name'],
step: step,
created_by: @user,
last_modified_by: @user
)
create_in_step!(step, checklist)
return unless params['items']
item_pos = 0
params['items'].each_value do |item_json|
ChecklistItem.create!(
text: item_json['text'],
checked: false,
position: item_pos,
created_by: @user,
last_modified_by: @user,
checklist: checklist
)
item_pos += 1
end
end
def remove_empty_inputs(obj)
obj.each_key do |key|
case obj[key]
when ''
obj[key] = nil
when Hash
# Recursive call
remove_empty_inputs(obj[key])
end
end
end
# create tiny_mce assets and change the import tokens
def populate_rte(params, object)
description = params['description'] || params['contents']
return description unless params['descriptionAssets']
params['descriptionAssets'].each_value do |tiny_mce_img_json|
tiny_mce_img = TinyMceAsset.new(
object: object,
team_id: @team.id,
saved: true
)
tiny_mce_img.save!
# Decode the file bytes
file = StringIO.new(Base64.decode64(tiny_mce_img_json['bytes']))
to_blob = ActiveStorage::Blob.create_and_upload!(
io: file,
filename: tiny_mce_img_json['fileName'],
content_type: tiny_mce_img_json['fileType'],
metadata: JSON.parse(tiny_mce_img_json['fileMetadata'] || '{}')
)
tiny_mce_img.image.attach(to_blob)
description.gsub!(
"data-mce-token=\"#{tiny_mce_img_json['tokenId']}\"",
"data-mce-token=\"#{Base62.encode(tiny_mce_img.id)}\""
) || description.gsub!(
"data-mce-token=\"#{Base62.encode(tiny_mce_img_json['tokenId'].to_i)}\"",
"data-mce-token=\"#{Base62.encode(tiny_mce_img.id)}\""
)
description.gsub!(' ]]--&gt;', '')
end
description
end
end

View file

@ -17,6 +17,9 @@
<div data-val="description" class="ql-editor"></div>
<hr>
<div class="row">
<div class="col-xs-12">
<div data-hold="step-texts"></div>
</div>
<div data-toggle="tables" class="col-xs-12">
<div data-hold="tables"></div>
</div>
@ -55,3 +58,7 @@
</label>
</div>
</div>
<div data-template="step-text" style="display: none;">
<div data-val="text"></div>
</div>