scinote-web/app/controllers/repository_rows_controller.rb
2019-12-06 11:12:07 +01:00

496 lines
17 KiB
Ruby

class RepositoryRowsController < ApplicationController
include InputSanitizeHelper
include ActionView::Helpers::TextHelper
include ApplicationHelper
before_action :load_info_modal_vars, only: :show
before_action :load_vars, only: %i(edit update)
before_action :load_repository,
only: %i(create
delete_records
index
copy_records
available_rows)
before_action :check_create_permissions, only: :create
before_action :check_manage_permissions,
only: %i(edit update delete_records copy_records)
def index
@draw = params[:draw].to_i
per_page = params[:length] == '-1' ? 100 : params[:length].to_i
page = (params[:start].to_i / per_page) + 1
records = RepositoryDatatableService.new(@repository,
params,
current_user)
@assigned_rows = records.assigned_rows
@repository_row_count = records.repository_rows.length
@columns_mappings = records.mappings
@repository_rows = records.repository_rows
.page(page)
.per(per_page)
.preload(
:repository_columns,
:created_by,
repository_cells: :value
)
end
def create
record = RepositoryRow.new(repository: @repository,
created_by: current_user,
last_modified_by: current_user)
errors = { default_fields: [],
repository_cells: [] }
record.transaction do
record.name = record_params[:repository_row_name] unless record_params[:repository_row_name].blank?
errors[:default_fields] = record.errors.messages unless record.save
if cell_params
cell_params.each do |key, value|
next if create_cell_value(record, key, value, errors).nil?
end
end
raise ActiveRecord::Rollback if errors[:repository_cells].any?
end
respond_to do |format|
format.json do
if errors[:default_fields].empty? && errors[:repository_cells].empty?
log_activity(:create_item_inventory, record)
render json: { id: record.id,
flash: t('repositories.create.success_flash',
record: escape_input(record.name),
repository: escape_input(@repository.name)) },
status: :ok
else
render json: errors,
status: :bad_request
end
end
end
end
def show
respond_to do |format|
format.json do
render json: {
html: render_to_string(
partial: 'repositories/repository_row_info_modal.html.erb'
)
}
end
end
end
def edit
json = {
repository_row: {
name: escape_input(@record.name),
repository_cells: {},
repository_column_items: fetch_columns_list_items
}
}
# Add custom cells ids as key (easier lookup on js side)
@record.repository_cells.each do |cell|
if cell.value_type == 'RepositoryAssetValue'
cell_value = cell.value.asset
else
cell_value = escape_input(cell.value.data)
end
json[:repository_row][:repository_cells][cell.repository_column_id] = {
repository_cell_id: cell.id,
cell_column_id: cell.repository_column.id, # needed for mappings
value: cell_value,
type: cell.value_type,
list_items: fetch_list_items(cell)
}
end
respond_to do |format|
format.html
format.json { render json: json }
end
end
def update
errors = {
default_fields: [],
repository_cells: []
}
@record.transaction do
@record.name = record_params[:repository_row_name].blank? ? nil : record_params[:repository_row_name]
errors[:default_fields] = @record.errors.messages unless @record.save
if cell_params
cell_params.each do |key, value|
existing = @record.repository_cells.detect do |c|
c.repository_column_id == key.to_i
end
if existing
# Cell exists and new value present, so update value
if existing.value_type == 'RepositoryListValue'
item = RepositoryListItem.where(
repository_column: existing.repository_column
).find(value) unless value == '-1'
if item
existing.value.update_attribute(
:repository_list_item_id, item.id
)
else
existing.delete
end
# ############################################ #
# Temporary, will be refactored in next ticket #
# ############################################ #
elsif existing.value_type == 'RepositoryStatusValue'
item = RepositoryStatusItem.where(
repository_column: existing.repository_column
).find(value) unless value == '-1'
if item
existing.value.update_attribute(
:repository_status_item_id, item.id
)
else
existing.delete
end
elsif existing.value_type == 'RepositoryAssetValue'
existing.value.destroy && next if value == '-1'
if existing.value.asset.update(file: value)
existing.value.asset.created_by = current_user
existing.value.asset.last_modified_by = current_user
existing.value.asset.post_process_file(current_team)
else
errors[:repository_cells] << {
"#{existing.repository_column_id}": { data: existing.value.asset.errors.messages[:file].first }
}
end
else
existing.value.destroy && next if value == ''
existing.value.data = value
if existing.value.save
record_annotation_notification(@record, existing)
else
errors[:repository_cells] << {
"#{existing.repository_column_id}":
existing.value.errors.messages
}
end
end
else
next if value == ''
# Looks like it is a new cell, so we need to create new value, cell
# will be created automatically
next if create_cell_value(@record, key, value, errors).nil?
end
end
else
@record.repository_cells.each { |c| c.value.destroy }
end
raise ActiveRecord::Rollback if errors[:repository_cells].any?
end
respond_to do |format|
format.json do
if errors[:default_fields].empty? && errors[:repository_cells].empty?
# Row sucessfully updated, so sending response to client
log_activity(:edit_item_inventory, @record)
render json: {
id: @record.id,
flash: t(
'repositories.update.success_flash',
record: escape_input(@record.name),
repository: escape_input(@repository.name)
)
},
status: :ok
else
# Errors
render json: errors,
status: :bad_request
end
end
end
end
def create_cell_value(record, key, value, errors)
column = @repository.repository_columns.detect do |c|
c.id == key.to_i
end
save_successful = false
if column.data_type == 'RepositoryListValue'
return if value == '-1'
# check if item exists else revert the transaction
list_item = RepositoryListItem.where(repository_column: column)
.find(value)
cell_value = RepositoryListValue.new(
repository_list_item_id: list_item.id,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
save_successful = list_item && cell_value.save
# ############################################ #
# Temporary, will be refactored in next ticket #
# ############################################ #
elsif column.data_type == 'RepositoryStatusValue'
return if value == '-1'
status_item = RepositoryStatusItem.where(repository_column: column).find(value)
cell_value = RepositoryStatusValue.new(
repository_status_item_id: status_item.id,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
save_successful = status_item && cell_value.save
elsif column.data_type == 'RepositoryAssetValue'
return if value.blank?
asset = Asset.new(file: value,
created_by: current_user,
last_modified_by: current_user,
team: current_team)
if asset.save
asset.post_process_file(current_team)
else
errors[:repository_cells] << {
"#{column.id}": { data: asset.errors.messages[:file].first }
}
end
cell_value = RepositoryAssetValue.new(
asset: asset,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
save_successful = cell_value.save
else
cell_value = RepositoryTextValue.new(
data: value,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
if (save_successful = cell_value.save)
record_annotation_notification(record,
cell_value.repository_cell)
end
end
unless save_successful
errors[:repository_cells] << {
"#{column.id}": cell_value.errors.messages
}
end
end
def delete_records
deleted_count = 0
if selected_params
selected_params.each do |row_id|
row = @repository.repository_rows.find_by_id(row_id)
next unless row && can_manage_repository_rows?(@repository)
log_activity(:delete_item_inventory, row)
row.destroy && deleted_count += 1
end
if deleted_count.zero?
flash = t('repositories.destroy.no_deleted_records_flash',
other_records_number: selected_params.count)
elsif deleted_count != selected_params.count
not_deleted_count = selected_params.count - deleted_count
flash = t('repositories.destroy.contains_other_records_flash',
records_number: deleted_count,
other_records_number: not_deleted_count)
else
flash = t('repositories.destroy.success_flash',
records_number: deleted_count)
end
respond_to do |format|
color = deleted_count.zero? ? 'info' : 'success'
format.json { render json: { flash: flash, color: color }, status: :ok }
end
else
respond_to do |format|
format.json do
render json: {
flash: t('repositories.destroy.no_records_selected_flash')
}, status: :bad_request
end
end
end
end
def copy_records
duplicate_service = RepositoryActions::DuplicateRows.new(
current_user, @repository, params[:selected_rows]
)
duplicate_service.call
render json: {
flash: t('repositories.copy_records_report',
number: duplicate_service.number_of_duplicated_items)
}, status: :ok
end
def available_rows
if @repository.repository_rows.empty?
no_items_string =
"#{t('projects.reports.new.save_PDF_to_inventory_modal.no_items')} " \
"#{link_to(t('projects.reports.new.save_PDF_to_inventory_modal.here'),
repository_path(@repository),
data: { 'no-turbolink' => true })}"
render json: { no_items: no_items_string },
status: :ok
else
render json: { results: load_available_rows(search_params[:q]) },
status: :ok
end
end
private
include StringUtility
AvailableRepositoryRow = Struct.new(:id, :name, :has_file_attached)
def load_info_modal_vars
@repository_row = RepositoryRow.eager_load(:created_by, repository: [:team])
.find_by_id(params[:id])
@assigned_modules = MyModuleRepositoryRow.eager_load(
my_module: [{ experiment: :project }]
).where(repository_row: @repository_row)
render_404 and return unless @repository_row
render_403 unless can_read_repository?(@repository_row.repository)
end
def load_vars
@repository = Repository.accessible_by_teams(current_team)
.eager_load(:repository_columns)
.find_by_id(params[:repository_id])
@record = @repository.repository_rows
.eager_load(:repository_columns)
.find_by_id(params[:id])
render_404 unless @repository && @record
end
def load_repository
@repository = Repository.accessible_by_teams(current_team).find_by_id(params[:repository_id])
render_404 unless @repository
render_403 unless can_read_repository?(@repository)
end
def check_create_permissions
render_403 unless can_create_repository_rows?(@repository)
end
def check_manage_permissions
render_403 unless can_manage_repository_rows?(@repository)
end
def record_params
params.permit(:repository_row_name).to_h
end
def cell_params
params.permit(repository_cells: {}).to_h[:repository_cells]
end
def remove_file_columns_params
JSON.parse(params.fetch(:remove_file_columns) { '[]' })
end
def selected_params
params.permit(selected_rows: []).to_h[:selected_rows]
end
def load_available_rows(query)
@repository.repository_rows
.includes(:repository_cells)
.name_like(search_params[:q])
.limit(Constants::SEARCH_LIMIT)
.select(:id, :name)
.collect do |row|
with_asset_cell = row.repository_cells.where(
'repository_cells.repository_column_id = ?',
search_params[:repository_column_id]
)
AvailableRepositoryRow.new(row.id,
ellipsize(row.name, 75, 50),
with_asset_cell.present?)
end
end
def search_params
params.permit(:q, :repository_id, :repository_column_id)
end
def record_annotation_notification(record, cell, old_text = nil)
table_url = params.fetch(:request_url) { :request_url_must_be_present }
smart_annotation_notification(
old_text: (old_text if old_text),
new_text: cell.value.data,
title: t('notifications.repository_annotation_title',
user: current_user.full_name,
column: cell.repository_column.name,
record: record.name,
repository: record.repository.name),
message: t('notifications.repository_annotation_message_html',
record: link_to(record.name, table_url),
column: link_to(cell.repository_column.name, table_url))
)
end
def fetch_list_items(cell)
return [] if cell.value_type != 'RepositoryListValue'
RepositoryListItem.where(repository: @repository)
.where(repository_column: cell.repository_column)
.limit(Constants::SEARCH_LIMIT)
.pluck(:id, :data)
.map { |li| [li[0], escape_input(li[1])] }
end
def fetch_columns_list_items
collection = []
@repository.repository_columns
.list_type
.preload(:repository_list_items)
.each do |column|
collection << {
column_id: column.id,
list_items: column.repository_list_items
.limit(Constants::SEARCH_LIMIT)
.pluck(:id, :data)
.map { |li| [li[0], escape_input(li[1])] }
}
end
collection
end
def log_activity(type_of, repository_row)
Activities::CreateActivityService
.call(activity_type: type_of,
owner: current_user,
subject: @repository,
team: current_team,
message_items: {
repository_row: repository_row.id,
repository: @repository.id
})
end
end