+
-
diff --git a/app/jobs/my_modules/due_date_reminder_job.rb b/app/jobs/my_modules/due_date_reminder_job.rb
index 839f4d07f..9975a5436 100644
--- a/app/jobs/my_modules/due_date_reminder_job.rb
+++ b/app/jobs/my_modules/due_date_reminder_job.rb
@@ -3,6 +3,7 @@
module MyModules
class DueDateReminderJob < ApplicationJob
def perform
+ NewRelic::Agent.ignore_transaction
my_modules = MyModule.uncomplete.approaching_due_dates
my_modules.each do |task|
diff --git a/app/jobs/notification_cleanup_job.rb b/app/jobs/notification_cleanup_job.rb
index cea9fa32f..31c2380d4 100644
--- a/app/jobs/notification_cleanup_job.rb
+++ b/app/jobs/notification_cleanup_job.rb
@@ -2,6 +2,7 @@
class NotificationCleanupJob < ApplicationJob
def perform
+ NewRelic::Agent.ignore_transaction
Notification.where('created_at < ?', 3.months.ago).delete_all
end
end
diff --git a/app/jobs/pdf_preview_job.rb b/app/jobs/pdf_preview_job.rb
index 8f161d226..1a9a22cb8 100644
--- a/app/jobs/pdf_preview_job.rb
+++ b/app/jobs/pdf_preview_job.rb
@@ -16,39 +16,9 @@ class PdfPreviewJob < ApplicationJob
def perform(asset_id)
asset = Asset.find(asset_id)
- blob = asset.file.blob
- blob.open(tmpdir: tempdir) do |input|
- work_dir = File.dirname(input.path)
- preview_filename = "#{File.basename(input.path, '.*')}.pdf"
- preview_file = File.join(work_dir, preview_filename)
- Rails.logger.info "Starting preparing document preview for file #{blob.filename.sanitized}..."
- ActiveRecord::Base.transaction do
- success = system(
- libreoffice_path, '--headless', '--invisible', '--convert-to', 'pdf', '--outdir', work_dir, input.path
- )
- unless success && File.file?(preview_file)
- raise StandardError, "There was an error generating PDF preview, blob id: #{blob.id}"
- end
-
- ActiveRecord::Base.no_touching do
- asset.file_pdf_preview.attach(io: File.open(preview_file), filename: "#{blob.filename.base}.pdf")
- asset.update(pdf_preview_processing: false)
- end
- Rails.logger.info("Finished preparing PDF preview for file #{blob.filename.sanitized}.")
- end
- ensure
- File.delete(preview_file) if File.file?(preview_file)
- end
- end
-
- private
-
- def tempdir
- Rails.root.join('tmp')
- end
-
- def libreoffice_path
- ENV['LIBREOFFICE_PATH'] || 'soffice'
+ PdfPreviewService.new(asset.file.blob, asset.file_pdf_preview).generate!
+ ensure
+ asset.update(pdf_preview_processing: false)
end
end
diff --git a/app/jobs/protocols/docx_import_job.rb b/app/jobs/protocols/docx_import_job.rb
index 49a34bbc5..dccd1a0dd 100644
--- a/app/jobs/protocols/docx_import_job.rb
+++ b/app/jobs/protocols/docx_import_job.rb
@@ -117,7 +117,7 @@ module Protocols
def create_step_asset_element!(step, step_element_json)
asset = @team.assets.new(created_by: @user, last_modified_by: @user)
# Decode the file bytes
- asset.file.attach(io: StringIO.new(Base64.decode64(step_element_json['contents'])), filename: 'file.blob')
+ asset.attach_file_version(io: StringIO.new(Base64.decode64(step_element_json['contents'])), filename: 'file.blob')
asset.save!
step.step_assets.create!(asset: asset)
asset.post_process_file
diff --git a/app/jobs/reports/docx_job.rb b/app/jobs/reports/docx_job.rb
index 51043ff37..1165653eb 100644
--- a/app/jobs/reports/docx_job.rb
+++ b/app/jobs/reports/docx_job.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'caracal'
+
module Reports
class DocxJob < ApplicationJob
extend InputSanitizeHelper
diff --git a/app/jobs/reports/docx_preview_job.rb b/app/jobs/reports/docx_preview_job.rb
index 5134bc529..9c35c6160 100644
--- a/app/jobs/reports/docx_preview_job.rb
+++ b/app/jobs/reports/docx_preview_job.rb
@@ -15,39 +15,7 @@ module Reports
def perform(report_id)
report = Report.find(report_id)
- blob = report.docx_file.blob
- blob.open(tmpdir: tempdir) do |input|
- work_dir = File.dirname(input.path)
- preview_filename = "#{File.basename(input.path, '.*')}.pdf"
- preview_file = File.join(work_dir, preview_filename)
- Rails.logger.info "Starting preparing document preview for file #{blob.filename.sanitized}..."
-
- ActiveRecord::Base.transaction do
- success = system(
- libreoffice_path, '--headless', '--invisible', '--convert-to', 'pdf', '--outdir', work_dir, input.path
- )
- unless success && File.file?(preview_file)
- raise StandardError, "There was an error generating PDF preview, blob id: #{blob.id}"
- end
-
- ActiveRecord::Base.no_touching do
- report.docx_preview_file.attach(io: File.open(preview_file), filename: "#{blob.filename.base}.pdf")
- end
- Rails.logger.info("Finished preparing PDF preview for file #{blob.filename.sanitized}.")
- end
- ensure
- File.delete(preview_file) if File.file?(preview_file)
- end
- end
-
- private
-
- def tempdir
- Rails.root.join('tmp')
- end
-
- def libreoffice_path
- ENV['LIBREOFFICE_PATH'] || 'soffice'
+ PdfPreviewService.new(report.docx_file.blob, report.docx_preview_file).generate!
end
end
end
diff --git a/app/jobs/repository_item_date_reminder_job.rb b/app/jobs/repository_item_date_reminder_job.rb
index 0ed56f118..555ca1e74 100644
--- a/app/jobs/repository_item_date_reminder_job.rb
+++ b/app/jobs/repository_item_date_reminder_job.rb
@@ -4,6 +4,7 @@ class RepositoryItemDateReminderJob < ApplicationJob
queue_as :default
def perform
+ NewRelic::Agent.ignore_transaction
process_repository_values(RepositoryDateTimeValue, DateTime.current)
process_repository_values(RepositoryDateValue, Date.current)
end
diff --git a/app/models/asset.rb b/app/models/asset.rb
index 0c2601d82..6841a795a 100644
--- a/app/models/asset.rb
+++ b/app/models/asset.rb
@@ -3,11 +3,11 @@
class Asset < ApplicationRecord
include SearchableModel
include DatabaseHelper
- include Encryptor
include WopiUtil
include ActiveStorageFileUtil
include ActiveStorageConcerns
include ActiveStorageHelper
+ include VersionedAttachments
require 'tempfile'
# Lock duration set to 30 minutes
@@ -17,9 +17,9 @@ class Asset < ApplicationRecord
enum view_mode: { thumbnail: 0, list: 1, inline: 2 }
# ActiveStorage configuration
- has_one_attached :file
+ has_one_versioned_attached :file
+ has_one_versioned_attached :preview_image
has_one_attached :file_pdf_preview
- has_one_attached :preview_image
# Asset validation
# This could cause some problems if you create empty asset and want to
@@ -145,41 +145,67 @@ class Asset < ApplicationRecord
file&.blob&.content_type
end
- def duplicate(new_name: nil)
+ def duplicate(new_name: nil, include_file_versions: false, created_by: nil)
new_asset = dup
file.filename = new_name if new_name
+ if created_by
+ new_asset.created_by = created_by
+ new_asset.last_modified_by = created_by
+ end
+
return unless new_asset.save
- duplicate_file(new_asset)
+ duplicate_file(new_asset, new_name: new_name, include_file_versions: include_file_versions)
+
new_asset
end
- def duplicate_file(to_asset)
+ def duplicate_blob(blob, attach_method, metadata: nil)
+ new_blob = nil
+
+ blob.open do |tmp_file|
+ new_blob = ActiveStorage::Blob.create_and_upload!(
+ io: tmp_file,
+ filename: blob.filename,
+ metadata: (metadata || blob.metadata)
+ )
+
+ attach_method.call(new_blob)
+ end
+
+ new_blob
+ end
+
+ def duplicate_file_versions(to_asset)
+ previous_files.map(&:blob).sort_by(&:created_at).each do |blob|
+ duplicate_blob(blob, to_asset.previous_files.method(:attach)) # .update_column(:created_at, blob.created_at)
+ end
+ end
+
+ def duplicate_file(to_asset, new_name: nil, include_file_versions: false)
return unless file.attached?
raise ArgumentError, 'Destination asset should be persisted first!' unless to_asset.persisted?
- file.blob.open do |tmp_file|
- to_blob = ActiveStorage::Blob.create_and_upload!(
- io: tmp_file,
- filename: blob.filename,
- metadata: blob.metadata
- )
- to_asset.file.attach(to_blob)
+ metadata = file.blob.metadata.dup
+
+ # set new name in metadata for OVE and MarvinJS files
+ if new_name && file.blob.metadata['asset_type'].in?(%w(gene_sequence marvinjs))
+ new_metadata_name = File.basename(new_name, File.extname(new_name))
+ metadata['name'] = new_metadata_name
end
- if preview_image.attached?
- preview_image.blob.open do |tmp_preview_image|
- to_blob = ActiveStorage::Blob.create_and_upload!(
- io: tmp_preview_image,
- filename: blob.filename,
- metadata: blob.metadata
- )
- to_asset.preview_image.attach(to_blob)
- end
+ unless include_file_versions
+ metadata.delete('version')
+ metadata.delete('restored_from_version')
+ metadata['created_by_id'] = to_asset.created_by_id
end
+ duplicate_file_versions(to_asset) if include_file_versions
+ duplicate_blob(blob, to_asset.method(:attach_file_version), metadata: metadata)
+ duplicate_blob(preview_image.blob, to_asset.method(:attach_preview_image_version)) if preview_image.attached?
+
to_asset.post_process_file
end
@@ -341,7 +367,7 @@ class Asset < ApplicationRecord
end
def update_contents(new_file)
- file.attach(io: new_file, filename: file_name)
+ attach_file_version(io: new_file, filename: file_name)
self.version = version.nil? ? 1 : version + 1
save
end
diff --git a/app/models/concerns/versioned_attachments.rb b/app/models/concerns/versioned_attachments.rb
new file mode 100644
index 000000000..5ec8f501c
--- /dev/null
+++ b/app/models/concerns/versioned_attachments.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module VersionedAttachments
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def has_one_versioned_attached(name)
+ has_one_attached name, dependent: :detach
+ has_many_attached "previous_#{name.to_s.pluralize}", dependent: :detach
+
+ define_method :"attach_#{name}_version" do |*args, **options|
+ ActiveRecord::Base.transaction(requires_new: true) do
+ __send__(:"previous_#{name.to_s.pluralize}").attach(__send__(name).blob) if __send__(name).attached?
+ __send__(name).attach(*args, **options)
+
+ new_blob = __send__(name).blob
+ new_blob.metadata['created_by_id'] ||= last_modified_by_id
+
+ # set version of current latest file if previous versions exist
+ new_blob.save! and next unless __send__(:"previous_#{name.to_s.pluralize}").any?
+
+ new_version =
+ (__send__(:"previous_#{name.to_s.pluralize}").last.blob.metadata['version'] || 1) + 1
+ new_blob.metadata['version'] = new_version
+ new_blob.save!
+ end
+ end
+
+ define_method :"restore_#{name}_version" do |version|
+ ActiveRecord::Base.transaction(requires_new: true) do
+ blob = __send__(:"previous_#{name.to_s.pluralize}").map(&:blob).find do |b|
+ (b.metadata['version'] || 1) == version
+ end
+
+ blob.open do |tmp_file|
+ new_blob = ActiveStorage::Blob.create_and_upload!(
+ io: tmp_file,
+ filename: blob.filename,
+ metadata: blob.metadata.merge({ 'restored_from_version' => version, 'created_by_id' => last_modified_by_id })
+ )
+
+ __send__(:"attach_#{name}_version", new_blob)
+ end
+ end
+ end
+ end
+ end
+
+ module_function
+
+ def enabled?
+ ApplicationSettings.instance.values['versioned_attachments_enabled']
+ end
+
+ def disabled_disclaimer
+ {
+ text: I18n.t('assets.file_versions_modal.disabled_disclaimer'),
+ button: I18n.t('assets.file_versions_modal.enable_button')
+ }
+ end
+end
diff --git a/app/models/linked_repository.rb b/app/models/linked_repository.rb
index a6874ad91..5e1872924 100644
--- a/app/models/linked_repository.rb
+++ b/app/models/linked_repository.rb
@@ -24,9 +24,9 @@ class LinkedRepository < Repository
'repository_rows.created_at',
'users.full_name',
'repository_rows.updated_at',
- 'last_modified_bies_repository_rows.full_name',
+ 'last_modified_by.full_name',
'repository_rows.archived_on',
- 'archived_bies_repository_rows.full_name',
+ 'archived_by.full_name',
'repository_rows.external_id'
]
end
diff --git a/app/models/my_module.rb b/app/models/my_module.rb
index 36b7578ff..5ded2c691 100644
--- a/app/models/my_module.rb
+++ b/app/models/my_module.rb
@@ -391,17 +391,17 @@ class MyModule < ApplicationRecord
{ data: data, headers: headers }
end
- def repository_docx_json(repository)
- headers = [
- I18n.t('repositories.table.id'),
- I18n.t('repositories.table.row_name'),
- I18n.t('repositories.table.added_on'),
- I18n.t('repositories.table.added_by')
- ]
+ def repository_docx_json(repository, excluded_columns)
+ headers = Report.default_repository_columns.filter_map do |key, value|
+ value unless excluded_columns.include?(key.to_s.to_i)
+ end
+
custom_columns = []
return false unless repository
repository.repository_columns.order(:id).each do |column|
+ next if excluded_columns.include?(column.id)
+
if column.data_type == 'RepositoryStockValue'
if repository.has_stock_consumption?
headers.push(I18n.t('repositories.table.row_consumption'))
@@ -416,7 +416,7 @@ class MyModule < ApplicationRecord
records = repository.assigned_rows(self)
.select(:id, :name, :created_at, :created_by_id, :repository_id, :parent_id, :archived)
- { headers: headers, rows: records, custom_columns: custom_columns }
+ { headers: headers, rows: records, custom_columns: custom_columns, excluded_columns: excluded_columns }
end
def deep_clone(current_user)
@@ -565,6 +565,12 @@ class MyModule < ApplicationRecord
yield
+ if status_changing_direction == :forward
+ my_module_status.my_module_status_consequences.each do |consequence|
+ consequence.before_forward_call(self)
+ end
+ end
+
if my_module_status.my_module_status_consequences.any?(&:runs_in_background?)
MyModuleStatusConsequencesJob
.perform_later(self, my_module_status.my_module_status_consequences.to_a, status_changing_direction)
diff --git a/app/models/my_module_status_consequence.rb b/app/models/my_module_status_consequence.rb
index b9b7b2f71..6abce4dc8 100644
--- a/app/models/my_module_status_consequence.rb
+++ b/app/models/my_module_status_consequence.rb
@@ -7,6 +7,8 @@ class MyModuleStatusConsequence < ApplicationRecord
def backward(my_module); end
+ def before_forward_call(my_module); end
+
def runs_in_background?
false
end
diff --git a/app/models/my_module_status_consequences/repository_snapshot.rb b/app/models/my_module_status_consequences/repository_snapshot.rb
index 30d47ce73..e39f8efeb 100644
--- a/app/models/my_module_status_consequences/repository_snapshot.rb
+++ b/app/models/my_module_status_consequences/repository_snapshot.rb
@@ -6,9 +6,14 @@ module MyModuleStatusConsequences
true
end
- def forward(my_module)
+ def before_forward_call(my_module)
my_module.assigned_repositories.each do |repository|
- repository_snapshot = ::RepositorySnapshot.create_preliminary!(repository, my_module)
+ ::RepositorySnapshot.create_preliminary!(repository, my_module)
+ end
+ end
+
+ def forward(my_module)
+ my_module.repository_snapshots.where(status: :provisioning).find_each do |repository_snapshot|
service = Repositories::SnapshotProvisioningService.call(repository_snapshot: repository_snapshot)
unless service.succeed?
diff --git a/app/models/protocol.rb b/app/models/protocol.rb
index 46156dc67..e16f385b0 100644
--- a/app/models/protocol.rb
+++ b/app/models/protocol.rb
@@ -70,27 +70,10 @@ class Protocol < ApplicationRecord
with_options if: :in_repository_published_version? do
validates :parent, presence: true
validate :parent_type_constraint
- validate :versions_same_name_constraint
end
with_options if: :in_repository_draft? do
# Only one draft can exist for each protocol
validate :ensure_single_draft
- validate :versions_same_name_constraint
- end
- with_options if: -> { in_repository? && !parent && !archived_changed?(from: false) } do |protocol|
- # Active protocol must have unique name inside its team
- protocol
- .validates_uniqueness_of :name, case_sensitive: false,
- scope: :team,
- conditions: lambda {
- where(
- protocol_type: [
- Protocol.protocol_types[:in_repository_published_original],
- Protocol.protocol_types[:in_repository_draft]
- ],
- parent_id: nil
- )
- }
end
with_options if: -> { in_repository? && archived? && !previous_version } do |protocol|
protocol.validates :archived_by, presence: true
@@ -351,7 +334,9 @@ class Protocol < ApplicationRecord
end
# Deep-clone given array of assets
- def self.deep_clone_assets(assets_to_clone)
+ # There is an issue with Delayed Job delayed methods, ruby 3, and keyword arguments (https://github.com/collectiveidea/delayed_job/issues/1134)
+ # so we're forced to use a normal argument with default value.
+ def self.deep_clone_assets(assets_to_clone, include_file_versions = false)
ActiveRecord::Base.no_touching do
assets_to_clone.each do |src_id, dest_id|
src = Asset.find_by(id: src_id)
@@ -360,12 +345,12 @@ class Protocol < ApplicationRecord
next unless src.present? && dest.present?
# Clone file
- src.duplicate_file(dest)
+ src.duplicate_file(dest, include_file_versions: include_file_versions)
end
end
end
- def self.clone_contents(src, dest, current_user, clone_keywords, only_contents = false)
+ def self.clone_contents(src, dest, current_user, clone_keywords, only_contents: false, include_file_versions: false)
dest.update(description: src.description, name: src.name) unless only_contents
src.clone_tinymce_assets(dest, dest.team)
@@ -382,7 +367,7 @@ class Protocol < ApplicationRecord
# Copy steps
src.steps.each do |step|
- step.duplicate(dest, current_user, step_position: step.position)
+ step.duplicate(dest, current_user, step_position: step.position, include_file_versions: include_file_versions)
end
end
@@ -615,7 +600,7 @@ class Protocol < ApplicationRecord
return draft if draft.invalid?
ActiveRecord::Base.no_touching do
- draft = deep_clone(draft, current_user)
+ draft = deep_clone(draft, current_user, include_file_versions: true)
end
parent_protocol.user_assignments.each do |parent_user_assignment|
@@ -760,7 +745,7 @@ class Protocol < ApplicationRecord
end
end
- def deep_clone(clone, current_user)
+ def deep_clone(clone, current_user, include_file_versions: false)
# Save cloned protocol first
success = clone.save
@@ -772,7 +757,7 @@ class Protocol < ApplicationRecord
raise ActiveRecord::RecordNotSaved unless success
- Protocol.clone_contents(self, clone, current_user, true, true)
+ Protocol.clone_contents(self, clone, current_user, true, only_contents: true, include_file_versions: include_file_versions)
clone.reload
clone
@@ -794,12 +779,6 @@ class Protocol < ApplicationRecord
end
end
- def versions_same_name_constraint
- if parent.present? && !parent.name.eql?(name)
- errors.add(:base, I18n.t('activerecord.errors.models.protocol.wrong_version_name'))
- end
- end
-
def version_number_constraint
if Protocol.where(protocol_type: Protocol::REPOSITORY_TYPES)
.where.not(id: id)
diff --git a/app/models/report.rb b/app/models/report.rb
index 0d1fbc97d..87d159e6a 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -43,6 +43,8 @@ class Report < ApplicationRecord
DEFAULT_SETTINGS = {
all_tasks: true,
+ exclude_task_metadata: false,
+ exclude_timestamps: false,
task: {
protocol: {
description: true,
@@ -62,8 +64,11 @@ class Report < ApplicationRecord
result_comments: true,
result_order: 'new',
activities: true,
- repositories: []
- }
+ repositories: [],
+ excluded_repository_columns: {}
+ },
+ template: 'scinote_template',
+ docx_template: 'scinote_template'
}.freeze
def self.search(
@@ -124,4 +129,13 @@ class Report < ApplicationRecord
ReportActions::ReportContent.new(report, content, {}, current_user).save_with_content
report
end
+
+ def self.default_repository_columns
+ {
+ '-1': I18n.t('repositories.table.id'),
+ '-2': I18n.t('repositories.table.row_name'),
+ '-3': I18n.t('repositories.table.added_on'),
+ '-4': I18n.t('repositories.table.added_by')
+ }
+ end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 166dd6f89..d60d072c3 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -83,9 +83,9 @@ class Repository < RepositoryBase
'repository_rows.created_at',
'users.full_name',
'repository_rows.updated_at',
- 'last_modified_bies_repository_rows.full_name',
+ 'last_modified_by.full_name',
'repository_rows.archived_on',
- 'archived_bies_repository_rows.full_name'
+ 'archived_by.full_name'
]
end
diff --git a/app/models/repository_asset_value.rb b/app/models/repository_asset_value.rb
index 5e9ba015f..9e4685aa6 100644
--- a/app/models/repository_asset_value.rb
+++ b/app/models/repository_asset_value.rb
@@ -60,9 +60,9 @@ class RepositoryAssetValue < ApplicationRecord
def update_data!(new_data, user)
if new_data.is_a?(String) # assume it's a signed_id_token
- asset.file.attach(new_data)
+ asset.attach_file_version(new_data)
elsif new_data[:file_data]
- asset.file.attach(io: StringIO.new(Base64.decode64(new_data[:file_data])), filename: new_data[:file_name])
+ asset.attach_file_version(io: StringIO.new(Base64.decode64(new_data[:file_data])), filename: new_data[:file_name])
end
asset.file_pdf_preview.purge if asset.file_pdf_preview.attached?
@@ -99,9 +99,9 @@ class RepositoryAssetValue < ApplicationRecord
value.asset = Asset.create!(created_by: value.created_by, last_modified_by: value.created_by, team: team)
if payload.is_a?(String) # assume it's a signed_id_token
- value.asset.file.attach(payload)
+ value.asset.attach_file_version(payload)
elsif payload[:file_data]
- value.asset.file.attach(io: StringIO.new(Base64.decode64(payload[:file_data])), filename: payload[:file_name])
+ value.asset.attach_file_version(io: StringIO.new(Base64.decode64(payload[:file_data])), filename: payload[:file_name])
end
value.asset.post_process_file
diff --git a/app/models/repository_cell.rb b/app/models/repository_cell.rb
index 9afda532d..73a9f3bd8 100644
--- a/app/models/repository_cell.rb
+++ b/app/models/repository_cell.rb
@@ -77,9 +77,9 @@ class RepositoryCell < ApplicationRecord
'"repository_date_time_values"."id" = "repository_cells"."value_id" AND ' \
'"repository_cells"."value_type" = \'RepositoryDateTimeValueBase\' ' \
'AND repository_reminder_columns.metadata ->> \'reminder_value\' <> \'\' AND ' \
- '(repository_date_time_values.data - NOW()) <= ' \
- '(repository_reminder_columns.metadata ->> \'reminder_value\')::int * ' \
- '(repository_reminder_columns.metadata ->> \'reminder_unit\')::int * interval \'1 sec\''
+ 'repository_date_time_values.data <= ' \
+ '(NOW() AT TIME ZONE \'UTC\') + (repository_reminder_columns.metadata ->> \'reminder_value\')::int * ' \
+ '(repository_reminder_columns.metadata ->> \'reminder_unit\')::int * \'1 SECOND\'::interval'
).joins(
'LEFT OUTER JOIN "hidden_repository_cell_reminders" ON ' \
'"repository_cells"."id" = "hidden_repository_cell_reminders"."repository_cell_id" AND ' \
diff --git a/app/models/soft_locked_repository.rb b/app/models/soft_locked_repository.rb
index 93a51178d..43e44c73d 100644
--- a/app/models/soft_locked_repository.rb
+++ b/app/models/soft_locked_repository.rb
@@ -8,4 +8,12 @@ class SoftLockedRepository < Repository
def shareable_write?
false
end
+
+ def unlocked?
+ @unlocked == true
+ end
+
+ def unlock!
+ @unlocked = true
+ end
end
diff --git a/app/models/step.rb b/app/models/step.rb
index 4652e0c47..395dfbc55 100644
--- a/app/models/step.rb
+++ b/app/models/step.rb
@@ -117,7 +117,7 @@ class Step < ApplicationRecord
step_texts.order(created_at: :asc).first
end
- def duplicate(protocol, user, step_position: nil, step_name: nil)
+ def duplicate(protocol, user, step_position: nil, step_name: nil, include_file_versions: false)
ActiveRecord::Base.transaction do
assets_to_clone = []
@@ -142,7 +142,7 @@ class Step < ApplicationRecord
# "Shallow" Copy assets
assets.each do |asset|
new_asset = asset.dup
- new_asset.save!
+ new_asset.update!(created_by: user, last_modified_by: user)
new_step.assets << new_asset
assets_to_clone << [asset.id, new_asset.id]
end
@@ -153,7 +153,7 @@ class Step < ApplicationRecord
end
# Call clone helper
- Protocol.delay(queue: :assets).deep_clone_assets(assets_to_clone)
+ Protocol.delay(queue: :assets).deep_clone_assets(assets_to_clone, include_file_versions)
new_step
end
diff --git a/app/models/team.rb b/app/models/team.rb
index 311fc27bb..ac4da78fc 100644
--- a/app/models/team.rb
+++ b/app/models/team.rb
@@ -221,6 +221,7 @@ class Team < ApplicationRecord
def create_default_label_templates
ZebraLabelTemplate.default.update(team: self, default: true)
+ ZebraLabelTemplate.default_203dpi.update(team: self, default: false)
FluicsLabelTemplate.default.update(team: self, default: true)
end
end
diff --git a/app/models/zebra_label_template.rb b/app/models/zebra_label_template.rb
index a3174b05a..619271a2a 100644
--- a/app/models/zebra_label_template.rb
+++ b/app/models/zebra_label_template.rb
@@ -11,4 +11,15 @@ class ZebraLabelTemplate < LabelTemplate
density: 12
)
end
+
+ def self.default_203dpi
+ ZebraLabelTemplate.new(
+ name: I18n.t('label_templates.default_zebra_name_203dpi'),
+ width_mm: 25.4,
+ height_mm: 12.7,
+ content: Extends::DEFAULT_LABEL_TEMPLATE_203DPI[:zpl],
+ unit: 0,
+ density: 12
+ )
+ end
end
diff --git a/app/permissions/asset.rb b/app/permissions/asset.rb
index bd6acb444..8f576ca09 100644
--- a/app/permissions/asset.rb
+++ b/app/permissions/asset.rb
@@ -27,11 +27,16 @@ Canaid::Permissions.register_for(Asset) do
if object.repository_column.repository.is_a?(RepositorySnapshot)
false
else
+ object.repository_row.active? &&
can_manage_repository_assets?(user, object.repository_column.repository)
end
end
end
+ can :restore_asset do |user, asset|
+ VersionedAttachments.enabled? && can_manage_asset?(user, asset)
+ end
+
can :open_asset_locally do |_user, asset|
ENV['ASSET_SYNC_URL'].present?
end
diff --git a/app/permissions/repository.rb b/app/permissions/repository.rb
index 3a7283793..738358423 100644
--- a/app/permissions/repository.rb
+++ b/app/permissions/repository.rb
@@ -25,10 +25,12 @@ Canaid::Permissions.register_for(Repository) do
create_repository_rows
manage_repository_rows
delete_repository_rows
- create_repository_columns)
+ create_repository_columns
+ manage_repository_columns)
.each do |perm|
can perm do |_, repository|
- repository.active? && repository.repository_snapshots.provisioning.none?
+ repository.active? && repository.repository_snapshots.provisioning.none? &&
+ (!repository.is_a?(SoftLockedRepository) || repository.unlocked?)
end
end
@@ -105,7 +107,7 @@ Canaid::Permissions.register_for(Repository) do
end
can :manage_repository_columns do |user, repository|
- repository.repository_snapshots.provisioning.none? && can_create_repository_columns?(user, repository)
+ repository.permission_granted?(user, RepositoryPermissions::COLUMNS_UPDATE)
end
# repository: create/update/delete filters
@@ -122,6 +124,10 @@ Canaid::Permissions.register_for(RepositoryColumn) do
# repository: update/delete field
# Tested in scope of RepositoryPermissions spec
can :manage_repository_column do |user, repository_column|
- repository_column.repository.repository_snapshots.provisioning.none? && can_create_repository_columns?(user, repository_column.repository)
+ repository_column.repository.repository_snapshots.provisioning.none? && repository_column.repository.permission_granted?(user, RepositoryPermissions::COLUMNS_UPDATE)
+ end
+
+ can :delete_repository_column do |user, repository_column|
+ repository_column.repository.repository_snapshots.provisioning.none? && repository_column.repository.permission_granted?(user, RepositoryPermissions::COLUMNS_DELETE)
end
end
diff --git a/app/serializers/active_storage/blob_serializer.rb b/app/serializers/active_storage/blob_serializer.rb
new file mode 100644
index 000000000..ff3a1ed8d
--- /dev/null
+++ b/app/serializers/active_storage/blob_serializer.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ class BlobSerializer < ActiveModel::Serializer
+ include Rails.application.routes.url_helpers
+
+ attributes :filename, :extension, :basename, :url, :created_at, :byte_size, :version, :restored_from_version, :created_by, :download_url
+
+ def basename
+ object.filename.base
+ end
+
+ def extension
+ object.filename.extension_without_delimiter
+ end
+
+ def download_url
+ rails_blob_url(object, disposition: 'attachment')
+ end
+
+ def version
+ object.metadata['version'] || 1
+ end
+
+ def restored_from_version
+ object.metadata['restored_from_version']
+ end
+
+ def created_at
+ return object.created_at unless @instance_options[:user]
+
+ object.created_at.strftime("#{@instance_options[:user].date_format}, %H:%M")
+ end
+
+ def created_by
+ User.select(:id, :full_name, :email).find_by(
+ id: object.metadata['created_by_id'] || object.attachments.first&.record&.created_by_id
+ )
+ end
+ end
+end
diff --git a/app/serializers/asset_serializer.rb b/app/serializers/asset_serializer.rb
index 9817f448a..d3408bf93 100644
--- a/app/serializers/asset_serializer.rb
+++ b/app/serializers/asset_serializer.rb
@@ -138,7 +138,8 @@ class AssetSerializer < ActiveModel::Serializer
load_asset: load_asset_path(object),
asset_file: asset_file_url_path(object),
marvin_js: marvin_js_asset_path(object),
- marvin_js_icon: image_path('icon_small/marvinjs.svg')
+ marvin_js_icon: image_path('icon_small/marvinjs.svg'),
+ versions: (asset_versions_path(object) if attached)
}
user = scope[:user] || @instance_options[:user]
if can_manage_asset?(user, object)
@@ -155,6 +156,7 @@ class AssetSerializer < ActiveModel::Serializer
)
end
+ urls[:restore_version] = asset_restore_version_path(object) if can_restore_asset?(user, object)
urls[:open_vector_editor_edit] = edit_gene_sequence_asset_path(object.id) if can_manage_asset?(user, object)
if can_manage_asset?(user, object) && can_open_asset_locally?(user, object)
diff --git a/app/serializers/notification_serializer.rb b/app/serializers/notification_serializer.rb
index ebb8ef838..75244c379 100644
--- a/app/serializers/notification_serializer.rb
+++ b/app/serializers/notification_serializer.rb
@@ -4,7 +4,7 @@ class NotificationSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
include BreadcrumbsHelper
- attributes :id, :title, :message, :created_at, :read_at, :type, :breadcrumbs, :checked, :today
+ attributes :id, :title, :message, :created_at, :read_at, :type, :breadcrumbs, :checked, :today, :toggle_read_url
def title
object.to_notification.title
@@ -31,4 +31,7 @@ class NotificationSerializer < ActiveModel::Serializer
object.read_at.present?
end
+ def toggle_read_url
+ toggle_read_user_notification_path(object)
+ end
end
diff --git a/app/services/label_printers/fluics/sync_service.rb b/app/services/label_printers/fluics/sync_service.rb
index 987a42c33..3510d3c48 100644
--- a/app/services/label_printers/fluics/sync_service.rb
+++ b/app/services/label_printers/fluics/sync_service.rb
@@ -9,6 +9,7 @@ module LabelPrinters
end
def sync_templates!
+ NewRelic::Agent.ignore_transaction
LabelPrinter.fluics.each do |printer|
api_client = ApiClient.new(printer.fluics_api_key)
templates = api_client.list_templates
diff --git a/app/services/marvin_js_service.rb b/app/services/marvin_js_service.rb
index 8713763ef..8541ed490 100644
--- a/app/services/marvin_js_service.rb
+++ b/app/services/marvin_js_service.rb
@@ -22,7 +22,7 @@ class MarvinJsService
asset = Asset.new(created_by: current_user,
last_modified_by: current_user,
team_id: current_team.id)
- attach_file(asset.file, file, params)
+ attach_file_version(asset, file, params)
asset.save!
asset.post_process_file
connect_asset(asset, params, current_user)
@@ -40,21 +40,14 @@ class MarvinJsService
file = generate_image(params)
asset.update(last_modified_by: current_user) if asset.is_a?(Asset)
- attach_file(attachment, file, params)
- asset.post_process_file if asset.instance_of?(Asset)
- asset
- end
- def update_file_name(new_name, asset_id, current_user, current_team)
- asset = current_team.assets.find(asset_id)
- prepared_name = prepare_name(new_name)
-
- ActiveRecord::Base.transaction do
- asset.last_modified_by = current_user
- asset.rename_file(prepared_name)
- asset.save!
+ if params[:object_type] == 'TinyMceAsset'
+ attach_file(attachment, file, params)
+ else
+ attach_file_version(asset, file, params)
end
+ asset.post_process_file if asset.instance_of?(Asset)
asset
end
@@ -90,6 +83,19 @@ class MarvinJsService
)
end
+ def attach_file_version(asset, file, params)
+ asset.attach_file_version(
+ io: file,
+ filename: "#{prepare_name(params[:name])}.jpg",
+ content_type: 'image/jpeg',
+ metadata: {
+ name: prepare_name(params[:name]),
+ description: params[:description],
+ asset_type: 'marvinjs'
+ }
+ )
+ end
+
def prepare_name(sketch_name)
if !sketch_name.blank?
sketch_name
diff --git a/app/services/pdf_preview_service.rb b/app/services/pdf_preview_service.rb
new file mode 100644
index 000000000..cb12e2159
--- /dev/null
+++ b/app/services/pdf_preview_service.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+class PdfPreviewService
+ def initialize(blob, attached)
+ @blob = blob
+ @attached = attached
+ end
+
+ def generate!
+ preview_file = nil
+
+ @blob.open(tmpdir: tempdir) do |input|
+ work_dir = File.dirname(input.path)
+ preview_filename = "#{File.basename(input.path, '.*')}.pdf"
+ preview_file = File.join(work_dir, preview_filename)
+ Rails.logger.info "Starting preparing document preview for file #{@blob.filename.sanitized}..."
+
+ ActiveRecord::Base.transaction do
+ success = system(
+ 'timeout',
+ Constants::PREVIEW_TIMEOUT_SECONDS.to_s,
+ libreoffice_path,
+ '--headless',
+ '--invisible',
+ '--convert-to',
+ 'pdf', '--outdir',
+ work_dir, input.path
+ )
+ unless success && File.file?(preview_file)
+ raise StandardError, "There was an error generating PDF preview, blob id: #{@blob.id}"
+ end
+
+ ActiveRecord::Base.no_touching do
+ @attached.attach(io: File.open(preview_file), filename: "#{@blob.filename.base}.pdf")
+ end
+ Rails.logger.info("Finished preparing PDF preview for file #{@blob.filename.sanitized}.")
+ end
+ end
+ ensure
+ File.delete(preview_file) if File.file?(preview_file)
+ end
+
+ private
+
+ def tempdir
+ Rails.root.join('tmp')
+ end
+
+ def libreoffice_path
+ ENV['LIBREOFFICE_PATH'] || 'soffice'
+ end
+end
diff --git a/app/services/report_actions/save_pdf_to_inventory_item.rb b/app/services/report_actions/save_pdf_to_inventory_item.rb
index 88b1e1a90..01866820f 100644
--- a/app/services/report_actions/save_pdf_to_inventory_item.rb
+++ b/app/services/report_actions/save_pdf_to_inventory_item.rb
@@ -44,7 +44,7 @@ module ReportActions
def create_new_asset
asset = Asset.create(created_by: @user, last_modified_by: @user, team: @team)
- asset.file.attach(@report.pdf_file.blob)
+ asset.attach_file_version(@report.pdf_file.blob)
asset
end
diff --git a/app/services/reports/docx.rb b/app/services/reports/docx.rb
index 7a0251371..72e6ce339 100644
--- a/app/services/reports/docx.rb
+++ b/app/services/reports/docx.rb
@@ -2,6 +2,10 @@
# rubocop:disable Style/ClassAndModuleChildren
+Dir[Rails.root.join('app/views/reports/docx_templates/**/docx.rb')].each do |file|
+ require file
+end
+
class Reports::Docx
include ActionView::Helpers::TextHelper
include ActionView::Helpers::UrlHelper
@@ -26,15 +30,23 @@ class Reports::Docx
@link_style = {}
@color = {}
@scinote_url = options[:scinote_url][0..-2]
+ @template = @settings[:docx_template].presence || 'scinote_template'
+
+ extend "#{@template.camelize}Docx".constantize
end
def draw
initial_document_load
- @report.root_elements.each do |subject|
- public_send("draw_#{subject.type_of}", subject)
- end
+ prepare_docx
+
@docx
end
+
+ private
+
+ def get_template_value(name)
+ @report.report_template_values.find_by(name: name)&.value
+ end
end
# rubocop:enable Style/ClassAndModuleChildren
diff --git a/app/services/reports/docx/draw_experiment.rb b/app/services/reports/docx/draw_experiment.rb
index e02d018a7..ebe3ae7ca 100644
--- a/app/services/reports/docx/draw_experiment.rb
+++ b/app/services/reports/docx/draw_experiment.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawExperiment
def draw_experiment(subject)
color = @color
link_style = @link_style
+ settings = @settings
scinote_url = @scinote_url
experiment = subject.experiment
return unless can_read_experiment?(@user, experiment)
@@ -14,12 +15,15 @@ module Reports::Docx::DrawExperiment
link_style
end
- @docx.p do
- text I18n.t('projects.reports.elements.experiment.user_time',
- code: experiment.code, timestamp: I18n.l(experiment.created_at, format: :full)), color: color[:gray]
- if experiment.archived?
- text ' | '
- text I18n.t('search.index.archived'), color: color[:gray]
+ if !settings['exclude_timestamps'] || experiment.archived?
+ @docx.p do
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.experiment.user_time',
+ code: experiment.code,
+ timestamp: I18n.l(experiment.created_at, format: :full)), color: color[:gray]
+ text ' | ' if experiment.archived?
+ end
+ text I18n.t('search.index.archived'), color: color[:gray] if experiment.archived?
end
end
html = custom_auto_link(experiment.description, team: @report_team)
diff --git a/app/services/reports/docx/draw_my_module.rb b/app/services/reports/docx/draw_my_module.rb
index ddfefd7df..bd859baab 100644
--- a/app/services/reports/docx/draw_my_module.rb
+++ b/app/services/reports/docx/draw_my_module.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
module Reports::Docx::DrawMyModule
- def draw_my_module(subject)
+ def draw_my_module(subject, without_results: false, without_repositories: false)
color = @color
link_style = @link_style
+ settings = @settings
scinote_url = @scinote_url
my_module = subject.my_module
tags = my_module.tags.order(:id)
@@ -15,45 +16,50 @@ module Reports::Docx::DrawMyModule
link_style
end
- @docx.p do
- text I18n.t('projects.reports.elements.module.user_time', code: my_module.code,
- timestamp: I18n.l(my_module.created_at, format: :full)), color: color[:gray]
- if my_module.archived?
- text ' | '
- text I18n.t('search.index.archived'), color: color[:gray]
- end
- end
-
- if my_module.started_on.present?
+ if my_module.archived? || !settings['exclude_timestamps']
@docx.p do
- text I18n.t('projects.reports.elements.module.started_on',
- started_on: I18n.l(my_module.started_on, format: :full))
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.module.user_time', code: my_module.code,
+ timestamp: I18n.l(my_module.created_at, format: :full)), color: color[:gray]
+ text ' | ' if my_module.archived?
+ end
+
+ text I18n.t('search.index.archived'), color: color[:gray] if my_module.archived?
end
end
- if my_module.due_date.present?
+ unless settings['exclude_task_metadata']
+ if my_module.started_on.present?
+ @docx.p do
+ text I18n.t('projects.reports.elements.module.started_on',
+ started_on: I18n.l(my_module.started_on, format: :full))
+ end
+ end
+
+ if my_module.due_date.present?
+ @docx.p do
+ text I18n.t('projects.reports.elements.module.due_date',
+ due_date: I18n.l(my_module.due_date, format: :full))
+ end
+ end
+
+ status = my_module.my_module_status
@docx.p do
- text I18n.t('projects.reports.elements.module.due_date',
- due_date: I18n.l(my_module.due_date, format: :full))
+ text I18n.t('projects.reports.elements.module.status')
+ text ' '
+ text "[#{status.name}]", color: (status.light_color? ? '000000' : status.color.delete('#'))
+ if my_module.completed?
+ text " #{I18n.t('my_modules.states.completed')} #{I18n.l(my_module.completed_on, format: :full)}"
+ end
end
- end
- status = my_module.my_module_status
- @docx.p do
- text I18n.t('projects.reports.elements.module.status')
- text ' '
- text "[#{status.name}]", color: (status.light_color? ? '000000' : status.color.delete('#'))
- if my_module.completed?
- text " #{I18n.t('my_modules.states.completed')} #{I18n.l(my_module.completed_on, format: :full)}"
- end
- end
-
- if tags.present?
- @docx.p do
- text I18n.t('projects.reports.elements.module.tags_header')
- tags.each do |tag|
- text ' '
- text "[#{tag.name}]", color: tag.color.delete('#')
+ if tags.present?
+ @docx.p do
+ text I18n.t('projects.reports.elements.module.tags_header')
+ tags.each do |tag|
+ text ' '
+ text "[#{tag.name}]", color: tag.color.delete('#')
+ end
end
end
end
@@ -69,31 +75,16 @@ module Reports::Docx::DrawMyModule
filter_steps_for_report(my_module.protocol.steps, @settings).order(:position).each do |step|
draw_step(step)
end
+ @docx.p
- if my_module.results.any? && (%w(file_results table_results text_results).any? { |k| @settings.dig('task', k) })
- @docx.h4 I18n.t('Results')
- order_results_for_report(my_module.results, @settings.dig('task', 'result_order')).each do |result|
- @docx.p do
- text result.name.presence || I18n.t('projects.reports.unnamed'), italic: true
- text " #{I18n.t('search.index.archived')} ", bold: true if result.archived?
- text I18n.t('projects.reports.elements.result.user_time',
- timestamp: I18n.l(result.created_at, format: :full),
- user: result.user.full_name), color: color[:gray]
- end
- draw_result_asset(result, @settings) if @settings.dig('task', 'file_results')
- result.result_orderable_elements.each do |element|
- if @settings.dig('task', 'table_results') && element.orderable_type == 'ResultTable'
- draw_result_table(element)
- elsif @settings.dig('task', 'text_results') && element.orderable_type == 'ResultText'
- draw_result_text(element)
- end
- end
- draw_result_comments(result) if @settings.dig('task', 'result_comments')
- end
+ unless without_results
+ draw_results(my_module)
+ @docx.p
end
- @docx.p
subject.children.active.each do |child|
+ next if without_repositories && child.type_of == 'my_module_repository'
+
public_send("draw_#{child.type_of}", child)
end
diff --git a/app/services/reports/docx/draw_my_module_protocol.rb b/app/services/reports/docx/draw_my_module_protocol.rb
index 2096569ff..0f3b56dbe 100644
--- a/app/services/reports/docx/draw_my_module_protocol.rb
+++ b/app/services/reports/docx/draw_my_module_protocol.rb
@@ -12,13 +12,14 @@ module Reports::Docx::DrawMyModuleProtocol
end
if @settings.dig('task', 'protocol', 'description') && protocol.description.present?
- @docx.p I18n.t('projects.reports.elements.module.protocol.user_time', code: protocol.original_code,
- timestamp: I18n.l(protocol.created_at, format: :full)), color: @color[:gray]
+ unless @settings['exclude_timestamps']
+ @docx.p I18n.t('projects.reports.elements.module.protocol.user_time', code: protocol.original_code,
+ timestamp: I18n.l(protocol.created_at, format: :full)), color: @color[:gray]
+ end
html = custom_auto_link(protocol.description, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
@docx.p
- @docx.p
end
end
end
diff --git a/app/services/reports/docx/draw_my_module_repository.rb b/app/services/reports/docx/draw_my_module_repository.rb
index b552211d1..11f1ed0c8 100644
--- a/app/services/reports/docx/draw_my_module_repository.rb
+++ b/app/services/reports/docx/draw_my_module_repository.rb
@@ -5,11 +5,12 @@ module Reports::Docx::DrawMyModuleRepository
my_module = subject.my_module
repository = subject.repository
repository = assigned_repository_or_snapshot(my_module, repository)
+ excluded_repository_columns = @settings.dig(:task, :excluded_repository_columns, repository.id.to_s) || {}
return unless repository && can_read_experiment?(@user, my_module.experiment) &&
(repository.is_a?(RepositorySnapshot) || can_read_repository?(@user, repository))
- repository_data = my_module.repository_docx_json(repository)
+ repository_data = my_module.repository_docx_json(repository, excluded_repository_columns)
return false unless repository_data[:rows].any? && can_read_repository?(@user, repository)
@@ -19,7 +20,12 @@ module Reports::Docx::DrawMyModuleRepository
@docx.p I18n.t('projects.reports.elements.module_repository.name',
repository: repository.name,
my_module: my_module.name), bold: true, size: Constants::REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE
- @docx.table table, border_size: Constants::REPORT_DOCX_TABLE_BORDER_SIZE
+
+ if table.present?
+ @docx.table table, border_size: Constants::REPORT_DOCX_TABLE_BORDER_SIZE
+ else
+ @docx.p I18n.t('projects.reports.elements.module_repository.no_columns'), italic: true
+ end
@docx.p
@docx.p
diff --git a/app/services/reports/docx/draw_project_header.rb b/app/services/reports/docx/draw_project_header.rb
index defa8e36e..c0bbffeef 100644
--- a/app/services/reports/docx/draw_project_header.rb
+++ b/app/services/reports/docx/draw_project_header.rb
@@ -15,10 +15,12 @@ module Reports::Docx::DrawProjectHeader
link_style
end
- @docx.p do
- text I18n.t('projects.reports.elements.project_header.user_time', code: project.code,
- timestamp: I18n.l(project.created_at, format: :full)), color: color[:gray]
- br
+ unless @settings['exclude_timestamps']
+ @docx.p do
+ text I18n.t('projects.reports.elements.project_header.user_time', code: project.code,
+ timestamp: I18n.l(project.created_at, format: :full)), color: color[:gray]
+ br
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_result_asset.rb b/app/services/reports/docx/draw_result_asset.rb
index 030de2591..fc690977f 100644
--- a/app/services/reports/docx/draw_result_asset.rb
+++ b/app/services/reports/docx/draw_result_asset.rb
@@ -25,11 +25,9 @@ module Reports::Docx::DrawResultAsset
end
text " #{I18n.t('search.index.archived')} ", bold: true if result.archived?
text ' ' + I18n.t('projects.reports.elements.result_asset.file_name', file: asset.file_name)
- text ' ' + I18n.t('projects.reports.elements.result_asset.user_time',
- user: result.user.full_name, timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
-
- if settings.dig(:task, :file_results_previews) && ActiveStorageFileUtil.previewable_document?(asset&.file&.blob)
- text " #{I18n.t('projects.reports.elements.result_asset.full_preview_attached')}", color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' ' + I18n.t('projects.reports.elements.result_asset.user_time',
+ user: result.user.full_name, timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
end
end
diff --git a/app/services/reports/docx/draw_result_comments.rb b/app/services/reports/docx/draw_result_comments.rb
index 3fc68ef1f..ebec9ad13 100644
--- a/app/services/reports/docx/draw_result_comments.rb
+++ b/app/services/reports/docx/draw_result_comments.rb
@@ -8,8 +8,10 @@ module Reports::Docx::DrawResultComments
@docx.p
@docx.p I18n.t('projects.reports.elements.result_comments.name', result: result.name),
bold: true, size: Constants::REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE
- comments.each do |comment|
+ comments.find_each.with_index do |comment, index|
comment_ts = comment.created_at
+
+ @docx.p unless index.zero?
@docx.p I18n.t('projects.reports.elements.result_comments.comment_prefix',
user: comment.user.full_name,
date: I18n.l(comment_ts, format: :full_date),
@@ -17,7 +19,6 @@ module Reports::Docx::DrawResultComments
html = custom_auto_link(comment.message, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
- @docx.p
end
end
end
diff --git a/app/services/reports/docx/draw_result_table.rb b/app/services/reports/docx/draw_result_table.rb
index 661d41a46..f919cc8a4 100644
--- a/app/services/reports/docx/draw_result_table.rb
+++ b/app/services/reports/docx/draw_result_table.rb
@@ -5,6 +5,7 @@ module Reports::Docx::DrawResultTable
result = element.result
table = element.orderable.table
timestamp = table.created_at
+ settings = @settings
color = @color
obj = self
table_data = JSON.parse(table.contents_utf_8)['data']
@@ -39,9 +40,11 @@ module Reports::Docx::DrawResultTable
end
@docx.p do
text I18n.t 'projects.reports.elements.result_table.table_name', name: table.name
- text ' '
- text I18n.t('projects.reports.elements.result_table.user_time',
- timestamp: I18n.l(timestamp, format: :full), user: result.user.full_name), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t('projects.reports.elements.result_table.user_time',
+ timestamp: I18n.l(timestamp, format: :full), user: result.user.full_name), color: color[:gray]
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_result_text.rb b/app/services/reports/docx/draw_result_text.rb
index 2ab606a7e..baf9bc9f3 100644
--- a/app/services/reports/docx/draw_result_text.rb
+++ b/app/services/reports/docx/draw_result_text.rb
@@ -4,12 +4,18 @@ module Reports::Docx::DrawResultText
def draw_result_text(element)
result_text = element.orderable
timestamp = element.created_at
+ settings = @settings
color = @color
- @docx.p do
- text result_text.name.presence || '', italic: true
- text ' '
- text I18n.t('projects.reports.elements.result_text.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ if result_text.name.present? || !settings['exclude_timestamps']
+ @docx.p do
+ text result_text.name.to_s, italic: true
+ text ' ' if result_text.name.present?
+
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.result_text.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
+ end
end
html = custom_auto_link(result_text.text, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
diff --git a/app/services/reports/docx/draw_results.rb b/app/services/reports/docx/draw_results.rb
new file mode 100644
index 000000000..a708c262e
--- /dev/null
+++ b/app/services/reports/docx/draw_results.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Reports::Docx::DrawResults
+ def draw_results(my_module, with_my_module_name: false)
+ color = @color
+ settings = @settings
+ scinote_url = @scinote_url
+ link_style = @link_style
+ return unless can_read_my_module?(@user, my_module)
+
+ if my_module.results.any? && (%w(file_results table_results text_results).any? { |k| @settings.dig('task', k) })
+ if with_my_module_name
+ @docx.h3 do
+ link my_module.name,
+ scinote_url + Rails.application.routes.url_helpers.protocols_my_module_path(my_module),
+ link_style
+ end
+ end
+ @docx.h4 I18n.t('Results')
+ order_results_for_report(my_module.results, @settings.dig('task', 'result_order')).each do |result|
+ @docx.p do
+ text result.name.presence || I18n.t('projects.reports.unnamed'), italic: true
+ text " #{I18n.t('search.index.archived')} ", bold: true if result.archived?
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.result.user_time',
+ timestamp: I18n.l(result.created_at, format: :full),
+ user: result.user.full_name), color: color[:gray]
+ end
+ end
+ draw_result_asset(result, @settings) if @settings.dig('task', 'file_results')
+ result.result_orderable_elements.each do |element|
+ if @settings.dig('task', 'table_results') && element.orderable_type == 'ResultTable'
+ draw_result_table(element)
+ elsif @settings.dig('task', 'text_results') && element.orderable_type == 'ResultText'
+ draw_result_text(element)
+ end
+ end
+ draw_result_comments(result) if @settings.dig('task', 'result_comments')
+ end
+ end
+ end
+end
diff --git a/app/services/reports/docx/draw_step.rb b/app/services/reports/docx/draw_step.rb
index c6cc90660..579a24c4e 100644
--- a/app/services/reports/docx/draw_step.rb
+++ b/app/services/reports/docx/draw_step.rb
@@ -6,22 +6,30 @@ module Reports::Docx::DrawStep
step_type_str = step.completed ? 'completed' : 'uncompleted'
user = (step.completed? && step.last_modified_by) || step.user
timestamp = step.completed ? step.completed_on : step.created_at
+ settings = @settings
@docx.p
- @docx.h5(
+ @docx.h4(
"#{I18n.t('projects.reports.elements.step.step_pos', pos: step.position_plus_one)} #{step.name}"
)
- @docx.p do
- if step.completed
- text I18n.t('protocols.steps.completed'), color: color[:green], bold: true
- else
- text I18n.t('protocols.steps.uncompleted'), color: color[:gray], bold: true
+
+ unless settings['exclude_task_metadata'] || settings['exclude_timestamps']
+ @docx.p do
+ unless settings['exclude_task_metadata']
+ if step.completed
+ text I18n.t('protocols.steps.completed'), color: color[:green], bold: true
+ else
+ text I18n.t('protocols.steps.uncompleted'), color: color[:gray], bold: true
+ end
+ end
+ unless settings['exclude_timestamps']
+ text ' | ' unless settings['exclude_task_metadata']
+ text I18n.t(
+ "projects.reports.elements.step.#{step_type_str}.user_time",
+ user: user.full_name,
+ timestamp: I18n.l(timestamp, format: :full)
+ ), color: color[:gray]
+ end
end
- text ' | '
- text I18n.t(
- "projects.reports.elements.step.#{step_type_str}.user_time",
- user: user.full_name,
- timestamp: I18n.l(timestamp, format: :full)
- ), color: color[:gray]
end
step.step_orderable_elements.order(:position).each do |element|
@@ -41,9 +49,6 @@ module Reports::Docx::DrawStep
end
draw_step_comments(step) if @settings.dig('task', 'protocol', 'step_comments')
-
- @docx.p
- @docx.p
end
def handle_step_table(table)
diff --git a/app/services/reports/docx/draw_step_asset.rb b/app/services/reports/docx/draw_step_asset.rb
index 8d83ca221..5b6569919 100644
--- a/app/services/reports/docx/draw_step_asset.rb
+++ b/app/services/reports/docx/draw_step_asset.rb
@@ -5,6 +5,7 @@ module Reports::Docx::DrawStepAsset
timestamp = asset.created_at
asset_url = Rails.application.routes.url_helpers.asset_download_url(asset)
color = @color
+ settings = @settings
@docx.p
begin
@@ -22,9 +23,11 @@ module Reports::Docx::DrawStepAsset
link I18n.t('projects.reports.elements.download'), asset_url do
italic true
end
- text ' '
- text I18n.t('projects.reports.elements.step_asset.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t('projects.reports.elements.step_asset.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_step_checklist.rb b/app/services/reports/docx/draw_step_checklist.rb
index e2662e60e..5c510c9f9 100644
--- a/app/services/reports/docx/draw_step_checklist.rb
+++ b/app/services/reports/docx/draw_step_checklist.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawStepChecklist
def draw_step_checklist(checklist)
team = @report_team
user = @user
+ settings = @settings
items = checklist.checklist_items
timestamp = checklist.created_at
@@ -15,9 +16,11 @@ module Reports::Docx::DrawStepChecklist
team,
I18n.t('projects.reports.elements.step_checklist.checklist_name', name: checklist.name)
).text, italic: true
- text ' '
- text I18n.t('projects.reports.elements.step_checklist.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t('projects.reports.elements.step_checklist.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
end
if items.any?
@docx.ul do
diff --git a/app/services/reports/docx/draw_step_comments.rb b/app/services/reports/docx/draw_step_comments.rb
index 87d0abc5f..f9733eaeb 100644
--- a/app/services/reports/docx/draw_step_comments.rb
+++ b/app/services/reports/docx/draw_step_comments.rb
@@ -8,8 +8,10 @@ module Reports::Docx::DrawStepComments
@docx.p
@docx.p I18n.t('projects.reports.elements.step_comments.name', step: step.name),
bold: true, size: Constants::REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE
- comments.each do |comment|
+ comments.find_each.with_index do |comment, index|
comment_ts = comment.created_at
+
+ @docx.p unless index.zero?
@docx.p I18n.t('projects.reports.elements.step_comments.comment_prefix',
user: comment.user.full_name,
date: I18n.l(comment_ts, format: :full_date),
@@ -17,7 +19,6 @@ module Reports::Docx::DrawStepComments
html = custom_auto_link(comment.message, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
- @docx.p
end
end
end
diff --git a/app/services/reports/docx/draw_step_table.rb b/app/services/reports/docx/draw_step_table.rb
index a0826741a..3565c200e 100644
--- a/app/services/reports/docx/draw_step_table.rb
+++ b/app/services/reports/docx/draw_step_table.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawStepTable
def draw_step_table(table, table_type)
color = @color
timestamp = table.created_at
+ settings = @settings
obj = self
table_data = JSON.parse(table.contents_utf_8)['data']
table_data = obj.add_headers_to_table(table_data, table_type == 'step_well_plates_table')
@@ -38,9 +39,11 @@ module Reports::Docx::DrawStepTable
end
@docx.p do
text I18n.t("projects.reports.elements.#{table_type}.table_name", name: table.name), italic: true
- text ' '
- text I18n.t("projects.reports.elements.#{table_type}.user_time",
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t("projects.reports.elements.#{table_type}.user_time",
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_step_text.rb b/app/services/reports/docx/draw_step_text.rb
index 978386a94..5fc364639 100644
--- a/app/services/reports/docx/draw_step_text.rb
+++ b/app/services/reports/docx/draw_step_text.rb
@@ -5,11 +5,18 @@ module Reports::Docx::DrawStepText
step_text = element.orderable
timestamp = element.created_at
color = @color
- @docx.p do
- text step_text.name.presence || '', italic: true
- text ' '
- text I18n.t('projects.reports.elements.result_text.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ settings = @settings
+
+ if step_text.name.present? || !settings['exclude_timestamps']
+ @docx.p do
+ text step_text.name.to_s, italic: true
+ text ' ' if step_text.name.present?
+
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.result_text.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
+ end
end
if step_text.text.present?
html = custom_auto_link(step_text.text, team: @report_team)
diff --git a/app/services/reports/docx/private_methods.rb b/app/services/reports/docx/private_methods.rb
index 8e5db9780..1136bc635 100644
--- a/app/services/reports/docx/private_methods.rb
+++ b/app/services/reports/docx/private_methods.rb
@@ -16,16 +16,6 @@ module Reports::Docx::PrivateMethods
bottom Constants::REPORT_DOCX_MARGIN_BOTTOM
end
- @docx.page_numbers true, align: :right
-
- insert_logo
-
- @docx.p do
- text I18n.t('projects.reports.new.generate_PDF.generated_on', timestamp: I18n.l(Time.zone.now, format: :full))
- end
-
- @docx.hr
-
generate_html_styles
end
diff --git a/app/services/reports/docx/repository_helper.rb b/app/services/reports/docx/repository_helper.rb
index ff25507ef..d8166ca9b 100644
--- a/app/services/reports/docx/repository_helper.rb
+++ b/app/services/reports/docx/repository_helper.rb
@@ -4,19 +4,25 @@ module Reports::Docx::RepositoryHelper
include InputSanitizeHelper
include ActionView::Helpers::NumberHelper
- def prepare_row_columns_for_docx(repository_data, my_module, repository)
+ def prepare_row_columns_for_docx(repository_data, my_module = nil, repository = nil)
+ return if repository_data[:headers].blank?
+
result = [repository_data[:headers]]
+ excluded_columns = repository_data[:excluded_columns]
+
repository_data[:rows].each do |record|
row = []
- row.push(record.code)
- row.push(escape_input(record.archived ? "#{record.name} [#{I18n.t('general.archived')}]" : record.name))
- row.push(I18n.l(record.created_at, format: :full))
- row.push(escape_input(record.created_by.full_name))
+ row.push(record.code) unless excluded_columns.include?(-1)
+ unless excluded_columns.include?(-2)
+ row.push(escape_input(record.archived ? "#{record.name} [#{I18n.t('general.archived')}]" : record.name))
+ end
+ row.push(I18n.l(record.created_at, format: :full)) unless excluded_columns.include?(-3)
+ row.push(escape_input(record.created_by.full_name)) unless excluded_columns.include?(-4)
cell_values = {}
custom_cells = record.repository_cells
custom_cells.each do |cell|
- if cell.value.instance_of? RepositoryStockValue
+ if cell.value.instance_of?(RepositoryStockValue) && my_module
if repository.is_a?(RepositorySnapshot)
consumed_stock = record.repository_stock_consumption_cell&.value&.formatted || 0
cell_values[cell.repository_column_id] = consumed_stock
@@ -38,6 +44,8 @@ module Reports::Docx::RepositoryHelper
end
repository_data[:custom_columns].each do |column_id|
+ next if excluded_columns.include?(column_id)
+
value = cell_values[column_id]
row.push(value)
end
diff --git a/app/services/reports/docx_renderer.rb b/app/services/reports/docx_renderer.rb
index 91872dc46..dd027bc4b 100644
--- a/app/services/reports/docx_renderer.rb
+++ b/app/services/reports/docx_renderer.rb
@@ -130,6 +130,13 @@ module Reports
row[:data].each do |cell|
docx_cell = Caracal::Core::Models::TableCellModel.new do |c|
cell.each do |content|
+ c.background content[:style][:background] if content.dig(:style, :background).present?
+ if content.dig(:style, :vertical_align).present? && content[:style][:vertical_align] != :middle
+ c.vertical_align content[:style][:vertical_align]
+ else
+ c.vertical_align :center
+ end
+
if content[:type] == 'p'
Reports::DocxRenderer.render_p_element(c, content, options.merge({ skip_br: true }))
elsif content[:type] == 'table'
diff --git a/app/services/reports/html_to_word_converter.rb b/app/services/reports/html_to_word_converter.rb
index f4e312f8e..6d589a020 100644
--- a/app/services/reports/html_to_word_converter.rb
+++ b/app/services/reports/html_to_word_converter.rb
@@ -208,7 +208,7 @@ module Reports
if style
style_keys.each do |key|
- style_el = style.value.split(';').select { |i| (i.include? key) }[0]
+ style_el = style.value.split(';').find { |i| i.strip.start_with?(key) }
next unless style_el
value = style_el.split(':')[1].strip if style_el
@@ -259,6 +259,29 @@ module Reports
}
end
+ def table_cell_styling(elem)
+ style = elem.attributes['style']
+ result = {}
+ style_keys = %w(background-color vertical-align background)
+
+ if style
+ style_keys.each do |key|
+ style_el = style.value.split(';').find { |i| (i.include? key) }
+ next unless style_el
+
+ value = style_el.split(':')[1].strip if style_el
+
+ case key
+ when 'background-color', 'background'
+ result[:background] = normalized_hex_color(value)
+ when 'vertical-align'
+ result[:vertical_align] = value.to_sym
+ end
+ end
+ end
+ result
+ end
+
def tiny_mce_table_element(table_element)
# array of elements
rows = table_element.css('tbody').first.children.map do |row|
@@ -267,11 +290,13 @@ module Reports
cells = row.children.map do |cell|
next unless cell.name == 'td'
+ style = table_cell_styling(cell)
# Parse cell content
formated_cell = recursive_children(cell.children, [], true)
# Combine text elements to single paragraph
formated_cell = combine_docx_elements(formated_cell)
+ formated_cell.each { |element| element[:style] = style } if style.present?
formated_cell
end.reject(&:blank?)
{ type: 'tr', data: cells }
diff --git a/app/services/repository_actions/duplicate_cell.rb b/app/services/repository_actions/duplicate_cell.rb
index e33cebaf6..4c370f7e7 100644
--- a/app/services/repository_actions/duplicate_cell.rb
+++ b/app/services/repository_actions/duplicate_cell.rb
@@ -25,7 +25,7 @@ module RepositoryActions
private
def repository_asset_value_extra_attributes(value)
- new_asset = @cell.value.asset.duplicate
+ new_asset = @cell.value.asset.duplicate(created_by: @user)
value.asset = new_asset
end
diff --git a/app/services/team_importer.rb b/app/services/team_importer.rb
index e600b451e..e81c651c6 100644
--- a/app/services/team_importer.rb
+++ b/app/services/team_importer.rb
@@ -934,7 +934,7 @@ class TeamImporter
user_id || find_user(asset.last_modified_by_id)
asset.team = team
asset.save!
- asset.file.attach(io: file, filename: File.basename(file))
+ asset.attach_file_version(io: file, filename: File.basename(file))
asset.post_process_file
@asset_mappings[orig_asset_id] = asset.id
@asset_counter += 1
diff --git a/app/services/templates_service.rb b/app/services/templates_service.rb
index 8bdb18caf..75a6d0c12 100644
--- a/app/services/templates_service.rb
+++ b/app/services/templates_service.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'newrelic_rpm'
+
class TemplatesService
def initialize(base_dir = nil)
@base_dir = base_dir ? base_dir : "#{Rails.root}/app/assets/templates"
@@ -49,6 +51,7 @@ class TemplatesService
end
def update_all_templates
+ NewRelic::Agent.ignore_transaction
processed_counter = 0
updated_counter = 0
Team.find_each do |team|
diff --git a/app/services/toolbars/repositories_service.rb b/app/services/toolbars/repositories_service.rb
index 34ac15a30..c66059b23 100644
--- a/app/services/toolbars/repositories_service.rb
+++ b/app/services/toolbars/repositories_service.rb
@@ -31,7 +31,7 @@ module Toolbars
private
def rename_action
- return unless @single && can_manage_repository?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless @single && can_manage_repository?(@repository)
{
name: :update,
@@ -67,7 +67,7 @@ module Toolbars
def archive_action
return unless @repositories.all? do |repository|
- can_archive_repository?(repository) && !@repository.is_a?(SoftLockedRepository)
+ can_archive_repository?(repository)
end
{
@@ -92,7 +92,7 @@ module Toolbars
def restore_action
return unless @repositories.all? do |repository|
- can_archive_repository?(repository) && !repository.is_a?(SoftLockedRepository)
+ can_archive_repository?(repository)
end
{
@@ -105,7 +105,7 @@ module Toolbars
end
def delete_action
- return unless @single && can_delete_repository?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless @single && can_delete_repository?(@repository)
{
name: :delete,
diff --git a/app/services/toolbars/repository_rows_service.rb b/app/services/toolbars/repository_rows_service.rb
index cfbbdf84e..ad0924727 100644
--- a/app/services/toolbars/repository_rows_service.rb
+++ b/app/services/toolbars/repository_rows_service.rb
@@ -42,7 +42,7 @@ module Toolbars
private
def restore_action
- return unless can_manage_repository_rows?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless can_manage_repository_rows?(@repository)
return unless @repository_rows.all?(&:archived?)
@@ -57,7 +57,7 @@ module Toolbars
end
def edit_action
- return unless can_manage_repository_rows?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless can_manage_repository_rows?(@repository)
return unless @repository_rows.all?(&:active?)
@@ -87,7 +87,7 @@ module Toolbars
end
def duplicate_action
- return unless can_create_repository_rows?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless can_create_repository_rows?(@repository)
return unless @repository_rows.all?(&:active?)
@@ -151,7 +151,7 @@ module Toolbars
end
def archive_action
- return unless can_manage_repository_rows?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless can_manage_repository_rows?(@repository)
return unless @repository_rows.all?(&:active?)
@@ -166,7 +166,7 @@ module Toolbars
end
def delete_action
- return unless can_delete_repository_rows?(@repository) && !@repository.is_a?(SoftLockedRepository)
+ return unless can_delete_repository_rows?(@repository)
return unless @repository_rows.all?(&:archived?)
diff --git a/app/utilities/encryptor.rb b/app/utilities/encryptor.rb
deleted file mode 100644
index b18637391..000000000
--- a/app/utilities/encryptor.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Encryptor
- def decrypt(data)
- return '' unless data.present?
- cipher = build_cipher(:decrypt, 'f5awRubeTUd2E*8duxum')
- cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final
- end
-
- def encrypt(data)
- return '' unless data.present?
- cipher = build_cipher(:encrypt, 'f5awRubeTUd2E*8duxum')
- Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m'))
- end
-
- def build_cipher(type, password)
- cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type)
- cipher.pkcs5_keyivgen(password)
- cipher
- end
-end
\ No newline at end of file
diff --git a/app/utilities/protocol_importers/attachments_builder.rb b/app/utilities/protocol_importers/attachments_builder.rb
index df6788f1d..f0c50b5ea 100644
--- a/app/utilities/protocol_importers/attachments_builder.rb
+++ b/app/utilities/protocol_importers/attachments_builder.rb
@@ -7,7 +7,7 @@ module ProtocolImporters
step_json[:attachments].map do |f|
asset = Asset.new(created_by: user, last_modified_by: user, team: team)
- asset.file.attach(io: URI.open(f[:url]), filename: f[:name])
+ asset.attach_file_version(io: URI(f[:url]).open, filename: f[:name])
asset
end
end
diff --git a/app/utilities/protocols_exporter_v2.rb b/app/utilities/protocols_exporter_v2.rb
index a4e2ba3ea..e7ff623fa 100644
--- a/app/utilities/protocols_exporter_v2.rb
+++ b/app/utilities/protocols_exporter_v2.rb
@@ -132,7 +132,7 @@ module ProtocolsExporterV2
"
\n" \
"#{asset.file_name}\n" \
"#{asset.content_type}\n" \
- "\n" \
+ "\n" \
"\n"
end
end
diff --git a/app/utilities/protocols_importer.rb b/app/utilities/protocols_importer.rb
index a66b8fe02..4738c8eb9 100644
--- a/app/utilities/protocols_importer.rb
+++ b/app/utilities/protocols_importer.rb
@@ -132,7 +132,7 @@ class ProtocolsImporter
)
# Decode the file bytes
- asset.file.attach(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
+ asset.attach_file_version(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
filename: asset_json['fileName'],
content_type: asset_json['fileType'],
metadata: JSON.parse(asset_json['fileMetadata'] || '{}'))
diff --git a/app/utilities/protocols_importer_v2.rb b/app/utilities/protocols_importer_v2.rb
index ba6e3b813..9d91f438c 100644
--- a/app/utilities/protocols_importer_v2.rb
+++ b/app/utilities/protocols_importer_v2.rb
@@ -109,7 +109,7 @@ class ProtocolsImporterV2
)
# Decode the file bytes
- asset.file.attach(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
+ asset.attach_file_version(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
filename: asset_json['fileName'],
content_type: asset_json['fileType'],
metadata: JSON.parse(asset_json['fileMetadata'] || '{}'))
diff --git a/app/views/design_elements/_breadcrumbs.html.erb b/app/views/design_elements/_breadcrumbs.html.erb
new file mode 100644
index 000000000..dc24c31d1
--- /dev/null
+++ b/app/views/design_elements/_breadcrumbs.html.erb
@@ -0,0 +1,8 @@
+
+
+<%= javascript_include_tag 'vue_design_system_breadcrumbs' %>
diff --git a/app/views/design_elements/index.html.erb b/app/views/design_elements/index.html.erb
index 523cfe07e..5f9585a19 100644
--- a/app/views/design_elements/index.html.erb
+++ b/app/views/design_elements/index.html.erb
@@ -14,6 +14,8 @@
<%= render partial: 'select' %>
+<%= render partial: 'breadcrumbs' %>
+
<%= render partial: 'modals' %>
<%= render partial: 'icons', locals: {icons_list: icons_list} %>
diff --git a/app/views/global_activities/references/_protocol.html.erb b/app/views/global_activities/references/_protocol.html.erb
index 1ff72f58b..930a249cf 100644
--- a/app/views/global_activities/references/_protocol.html.erb
+++ b/app/views/global_activities/references/_protocol.html.erb
@@ -9,7 +9,7 @@
<% if subject.in_repository? %>
- <%= route_to_other_team protocols_path(team: subject.team),
+ <%= route_to_other_team protocol_path(subject),
team,
subject.name&.truncate(Constants::NAME_TRUNCATION_LENGTH),
title: subject.name %>
diff --git a/app/views/layouts/reports/template_values_editor.html.erb b/app/views/layouts/reports/template_values_editor.html.erb
index 1f9352e83..841b024ef 100644
--- a/app/views/layouts/reports/template_values_editor.html.erb
+++ b/app/views/layouts/reports/template_values_editor.html.erb
@@ -1,6 +1,10 @@