179 lines
4.7 KiB
Ruby
179 lines
4.7 KiB
Ruby
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
|
|
|
class KnowledgeBase::Category < ApplicationModel
|
|
include HasTranslations
|
|
include HasAgentAllowedParams
|
|
include ChecksKbClientNotification
|
|
include ChecksKbClientVisibility
|
|
|
|
AGENT_ALLOWED_ATTRIBUTES = %i[knowledge_base_id parent_id category_icon].freeze
|
|
AGENT_ALLOWED_NESTED_RELATIONS = %i[translations].freeze
|
|
|
|
belongs_to :knowledge_base, inverse_of: :categories
|
|
|
|
has_many :answers, class_name: 'KnowledgeBase::Answer',
|
|
inverse_of: :category,
|
|
dependent: :restrict_with_exception
|
|
|
|
has_many :children, class_name: 'KnowledgeBase::Category',
|
|
foreign_key: :parent_id,
|
|
inverse_of: :parent,
|
|
dependent: :restrict_with_exception
|
|
|
|
belongs_to :parent, class_name: 'KnowledgeBase::Category',
|
|
inverse_of: :children,
|
|
touch: true,
|
|
optional: true
|
|
|
|
has_many :permissions, class_name: 'KnowledgeBase::Permission',
|
|
as: :permissionable,
|
|
autosave: true,
|
|
dependent: :destroy
|
|
|
|
validates :category_icon, presence: true
|
|
|
|
scope :root, -> { where(parent: nil) }
|
|
scope :sorted, -> { order(position: :asc) }
|
|
|
|
acts_as_list scope: :parent, top_of_list: 0
|
|
|
|
alias assets_essential assets
|
|
|
|
def assets(data = {})
|
|
return data if assets_added_to?(data)
|
|
|
|
data = super(data)
|
|
data = knowledge_base.assets(data)
|
|
|
|
# include all siblings to make sure ordering is always up to date
|
|
data = ApplicationModel::CanAssets.reduce(assets_siblings, data)
|
|
data = ApplicationModel::CanAssets.reduce(translations, data)
|
|
|
|
# include parent category or KB for root to have full path
|
|
(parent || knowledge_base).assets(data)
|
|
end
|
|
|
|
def self_parent?(candidate)
|
|
return true if candidate == parent
|
|
return true if parent&.self_parent?(candidate)
|
|
end
|
|
|
|
def self_with_children
|
|
[self] + children.map(&:self_with_children).flatten
|
|
end
|
|
|
|
def self_with_parents
|
|
result = [self]
|
|
|
|
check = self
|
|
while check.parent.present?
|
|
result << check.parent
|
|
check = check.parent
|
|
end
|
|
|
|
result
|
|
end
|
|
|
|
def self_with_children_answers
|
|
KnowledgeBase::Answer.where(category_id: self_with_children_ids)
|
|
end
|
|
|
|
def self_with_children_ids
|
|
output = [id]
|
|
|
|
output << KnowledgeBase::Category.where(parent_id: output.last).pluck(:id) while output.last.present?
|
|
|
|
output.flatten
|
|
end
|
|
|
|
def full_destroy!
|
|
transaction do
|
|
answers.each(&:destroy!)
|
|
answers.reset
|
|
children.reset
|
|
destroy!
|
|
end
|
|
end
|
|
|
|
def public_content?(kb_locale = nil)
|
|
scope = self_with_children_answers.published
|
|
|
|
scope = scope.localed(kb_locale.system_locale) if kb_locale
|
|
|
|
scope.any?
|
|
end
|
|
|
|
def internal_content?(kb_locale = nil)
|
|
scope = self_with_children_answers.internal
|
|
|
|
scope = scope.localed(kb_locale.system_locale) if kb_locale
|
|
|
|
scope.any?
|
|
end
|
|
|
|
def visible?(kb_locale = nil)
|
|
public_content?(kb_locale)
|
|
end
|
|
|
|
def api_url
|
|
Rails.application.routes.url_helpers.knowledge_base_category_path(knowledge_base, self)
|
|
end
|
|
|
|
def permissions_effective
|
|
cache_key = KnowledgeBase::Permission.cache_key self
|
|
|
|
Rails.cache.fetch cache_key do
|
|
KnowledgeBase::Category::Permission.new(self).permissions_effective
|
|
end
|
|
end
|
|
|
|
def attributes_with_association_ids
|
|
attrs = super
|
|
attrs[:permissions_effective] = permissions_effective
|
|
attrs
|
|
end
|
|
|
|
private
|
|
|
|
def assets_siblings(siblings: sibling_categories, current_user: User.lookup(id: UserInfo.current_user_id))
|
|
granular = KnowledgeBase.granular_permissions?
|
|
|
|
if !granular && !current_user&.permissions?('knowledge_base.editor')
|
|
siblings.select(&:internal_content?)
|
|
elsif granular
|
|
siblings.select { |elem| assets_siblings_applicable?(elem, current_user) }
|
|
else
|
|
siblings
|
|
end
|
|
end
|
|
|
|
def assets_siblings_applicable?(elem, current_user)
|
|
ep = KnowledgeBase::EffectivePermission.new(current_user, elem)
|
|
|
|
case ep.access_effective
|
|
when 'none'
|
|
false
|
|
when 'reader'
|
|
elem.internal_content?
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
def cannot_be_child_of_parent
|
|
errors.add(:parent_id, 'cannot be a child of the parent') if self_parent?(self)
|
|
end
|
|
validate :cannot_be_child_of_parent
|
|
|
|
def reordering_callback
|
|
return if !parent_id_changed? && !position_changed?
|
|
|
|
# drop siblings cache to make sure ordering is always up to date
|
|
sibling_categories.each(&:cache_delete)
|
|
end
|
|
before_save :reordering_callback
|
|
|
|
def sibling_categories
|
|
parent&.children || knowledge_base.categories.root
|
|
end
|
|
end
|