mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-15 03:49:24 +08:00
216 lines
7.1 KiB
Ruby
216 lines
7.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class StorageLocation < ApplicationRecord
|
|
include Cloneable
|
|
include Discard::Model
|
|
ID_PREFIX = 'SL'
|
|
include PrefixedIdModel
|
|
include Shareable
|
|
include SearchableModel
|
|
|
|
default_scope -> { kept }
|
|
|
|
has_one_attached :image
|
|
|
|
belongs_to :team
|
|
belongs_to :parent, class_name: 'StorageLocation', optional: true
|
|
belongs_to :created_by, class_name: 'User'
|
|
|
|
has_many :storage_location_repository_rows, inverse_of: :storage_location, dependent: :destroy
|
|
has_many :storage_locations, foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
|
|
has_many :repository_rows, through: :storage_location_repository_rows
|
|
|
|
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
|
validate :parent_same_team, if: -> { parent.present? }
|
|
validate :parent_validation, if: -> { parent.present? }
|
|
validate :no_grid_options, if: -> { !container }
|
|
validate :no_dimensions, if: -> { !with_grid? }
|
|
|
|
scope :readable_by_user, (lambda do |user, team = user.current_team|
|
|
next StorageLocation.none unless team.permission_granted?(user, TeamPermissions::STORAGE_LOCATIONS_READ)
|
|
|
|
where(team: team)
|
|
end)
|
|
|
|
after_discard do
|
|
StorageLocation.where(parent_id: id).find_each(&:discard)
|
|
storage_location_repository_rows.each(&:discard)
|
|
end
|
|
|
|
def shared_with?(team)
|
|
return false if self.team == team
|
|
|
|
(root? ? self : root_storage_location).private_shared_with?(team)
|
|
end
|
|
|
|
def root?
|
|
parent_id.nil?
|
|
end
|
|
|
|
def root_storage_location
|
|
return self if root?
|
|
|
|
storage_location = self
|
|
|
|
storage_location = storage_location.parent while storage_location.parent_id
|
|
|
|
storage_location
|
|
end
|
|
|
|
def empty?
|
|
storage_location_repository_rows.count.zero?
|
|
end
|
|
|
|
def duplicate!(user, team)
|
|
ActiveRecord::Base.transaction do
|
|
new_storage_location = dup
|
|
new_storage_location.name = next_clone_name
|
|
new_storage_location.team = team unless parent_id
|
|
new_storage_location.created_by = user
|
|
new_storage_location.save!
|
|
copy_image(self, new_storage_location)
|
|
recursive_duplicate(id, new_storage_location.id, user, new_storage_location.team)
|
|
new_storage_location
|
|
rescue ActiveRecord::RecordInvalid
|
|
false
|
|
end
|
|
end
|
|
|
|
def with_grid?
|
|
metadata['display_type'] == 'grid'
|
|
end
|
|
|
|
def grid_size
|
|
metadata['dimensions'] if with_grid?
|
|
end
|
|
|
|
def available_positions
|
|
return unless with_grid?
|
|
|
|
occupied_positions = storage_location_repository_rows.pluck(:metadata).map { |metadata| metadata['position'] }
|
|
|
|
rows = {}
|
|
|
|
grid_size[0].times do |row|
|
|
rows_cells = []
|
|
grid_size[1].times.filter_map do |col|
|
|
rows_cells.push(col + 1) if occupied_positions.exclude?([row + 1, col + 1])
|
|
end
|
|
rows[row + 1] = rows_cells unless rows_cells.empty?
|
|
end
|
|
|
|
rows
|
|
end
|
|
|
|
def self.shared_sql_select(user)
|
|
shared_write_value = TeamSharedObject.permission_levels['shared_write']
|
|
team_id = user.current_team.id
|
|
|
|
case_statement = <<-SQL.squish
|
|
CASE
|
|
WHEN EXISTS (
|
|
SELECT 1 FROM team_shared_objects
|
|
WHERE team_shared_objects.shared_object_id = storage_locations.id
|
|
AND team_shared_objects.shared_object_type = 'StorageLocation'
|
|
AND storage_locations.team_id = :team_id
|
|
) THEN 1
|
|
WHEN EXISTS (
|
|
SELECT 1 FROM team_shared_objects
|
|
WHERE team_shared_objects.shared_object_id = storage_locations.id
|
|
AND team_shared_objects.shared_object_type = 'StorageLocation'
|
|
AND team_shared_objects.team_id = :team_id
|
|
) THEN
|
|
CASE
|
|
WHEN EXISTS (
|
|
SELECT 1 FROM team_shared_objects
|
|
WHERE team_shared_objects.shared_object_id = storage_locations.id
|
|
AND team_shared_objects.shared_object_type = 'StorageLocation'
|
|
AND team_shared_objects.permission_level = :shared_write_value
|
|
AND team_shared_objects.team_id = :team_id
|
|
) THEN 2
|
|
ELSE 3
|
|
END
|
|
ELSE 4
|
|
END as shared
|
|
SQL
|
|
|
|
ActiveRecord::Base.sanitize_sql_array(
|
|
[case_statement, { team_id: team_id, shared_write_value: shared_write_value }]
|
|
)
|
|
end
|
|
|
|
def self.storage_locations_enabled?
|
|
ApplicationSettings.instance.values['storage_locations_enabled']
|
|
end
|
|
|
|
private
|
|
|
|
def recursive_duplicate(old_parent_id = nil, new_parent_id = nil, user = nil, team = nil)
|
|
StorageLocation.where(parent_id: old_parent_id).find_each do |child|
|
|
new_child = child.dup
|
|
new_child.parent_id = new_parent_id
|
|
new_child.team = team
|
|
new_child.created_by = user
|
|
new_child.save!
|
|
copy_image(child, new_child)
|
|
recursive_duplicate(child.id, new_child.id, user, team)
|
|
end
|
|
end
|
|
|
|
def copy_image(old_storage_location, new_storage_location)
|
|
return unless old_storage_location.image.attached?
|
|
|
|
old_blob = old_storage_location.image.blob
|
|
old_blob.open do |tmp_file|
|
|
to_blob = ActiveStorage::Blob.create_and_upload!(
|
|
io: tmp_file,
|
|
filename: old_blob.filename,
|
|
metadata: old_blob.metadata
|
|
)
|
|
new_storage_location.image.attach(to_blob)
|
|
end
|
|
end
|
|
|
|
def self.inner_storage_locations(team, storage_location = nil)
|
|
entry_point_condition = storage_location ? 'parent_id = ?' : 'parent_id IS NULL'
|
|
|
|
inner_storage_locations_sql =
|
|
"WITH RECURSIVE inner_storage_locations(id, selected_storage_locations_ids) AS (
|
|
SELECT id, ARRAY[id]
|
|
FROM storage_locations
|
|
WHERE team_id = ? AND #{entry_point_condition}
|
|
UNION ALL
|
|
SELECT storage_locations.id, selected_storage_locations_ids || storage_locations.id
|
|
FROM inner_storage_locations
|
|
JOIN storage_locations ON storage_locations.parent_id = inner_storage_locations.id
|
|
WHERE NOT storage_locations.id = ANY(selected_storage_locations_ids)
|
|
)
|
|
SELECT id FROM inner_storage_locations ORDER BY selected_storage_locations_ids".gsub(/\n|\t/, ' ').squeeze(' ')
|
|
|
|
if storage_location.present?
|
|
where("storage_locations.id IN (#{inner_storage_locations_sql})", team.id, storage_location.id)
|
|
else
|
|
where("storage_locations.id IN (#{inner_storage_locations_sql})", team.id)
|
|
end
|
|
end
|
|
|
|
def parent_validation
|
|
if parent.id == id
|
|
errors.add(:parent, I18n.t('activerecord.errors.models.storage_location.attributes.parent_storage_location'))
|
|
elsif StorageLocation.inner_storage_locations(team, self).exists?(id: parent_id)
|
|
errors.add(:parent, I18n.t('activerecord.errors.models.project_folder.attributes.parent_storage_location_child'))
|
|
end
|
|
end
|
|
|
|
def no_grid_options
|
|
errors.add(:metadata, I18n.t('activerecord.errors.models.storage_location.attributes.metadata.invalid')) if metadata['display_type'] || metadata['dimensions']
|
|
end
|
|
|
|
def parent_same_team
|
|
errors.add(:parent, I18n.t('activerecord.errors.models.storage_location.attributes.parent_storage_location_team')) if parent.team != team
|
|
end
|
|
|
|
def no_dimensions
|
|
errors.add(:metadata, I18n.t('activerecord.errors.models.storage_location.attributes.metadata.invalid')) if !with_grid? && metadata['dimensions']
|
|
end
|
|
end
|