2022-01-01 13:38:12 +00:00
|
|
|
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
2021-06-01 12:20:20 +00:00
|
|
|
|
2019-06-04 03:40:48 +00:00
|
|
|
class KnowledgeBase::Category < ApplicationModel
|
|
|
|
include HasTranslations
|
|
|
|
include HasAgentAllowedParams
|
|
|
|
include ChecksKbClientNotification
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2020-06-08 07:17:29 +00:00
|
|
|
has_many :answers, class_name: 'KnowledgeBase::Answer',
|
|
|
|
inverse_of: :category,
|
|
|
|
dependent: :restrict_with_exception
|
2019-06-04 03:40:48 +00:00
|
|
|
|
|
|
|
has_many :children, class_name: 'KnowledgeBase::Category',
|
|
|
|
foreign_key: :parent_id,
|
|
|
|
inverse_of: :parent,
|
|
|
|
dependent: :restrict_with_exception
|
|
|
|
|
2020-06-08 07:17:29 +00:00
|
|
|
belongs_to :parent, class_name: 'KnowledgeBase::Category',
|
|
|
|
inverse_of: :children,
|
|
|
|
touch: true,
|
|
|
|
optional: true
|
2019-06-04 03:40:48 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
siblings = sibling_categories
|
|
|
|
|
|
|
|
if !User.lookup(id: UserInfo.current_user_id)&.permissions?('knowledge_base.editor')
|
|
|
|
siblings = siblings.select(&:internal_content?)
|
|
|
|
end
|
|
|
|
|
|
|
|
data = ApplicationModel::CanAssets.reduce(siblings, data)
|
|
|
|
data = ApplicationModel::CanAssets.reduce(translations, data)
|
|
|
|
|
|
|
|
# include parent category or KB for root to have full path
|
2019-06-28 11:38:49 +00:00
|
|
|
(parent || knowledge_base).assets(data)
|
2019-06-04 03:40:48 +00:00
|
|
|
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
|
|
|
|
|
2021-05-31 12:59:49 +00:00
|
|
|
def self_with_parents
|
|
|
|
result = [self]
|
|
|
|
|
|
|
|
check = self
|
|
|
|
while check.parent.present?
|
|
|
|
result << check.parent
|
|
|
|
check = check.parent
|
|
|
|
end
|
|
|
|
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2019-06-04 03:40:48 +00:00
|
|
|
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
|
|
|
|
|
2020-08-05 13:48:41 +00:00
|
|
|
scope = scope.localed(kb_locale.system_locale) if kb_locale
|
2019-06-04 03:40:48 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
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?
|
|
|
|
|
2019-07-31 08:23:48 +00:00
|
|
|
# drop siblings cache to make sure ordering is always up to date
|
2019-06-04 03:40:48 +00:00
|
|
|
sibling_categories.each(&:cache_delete)
|
|
|
|
end
|
|
|
|
before_save :reordering_callback
|
|
|
|
|
|
|
|
def sibling_categories
|
|
|
|
parent&.children || knowledge_base.categories.root
|
|
|
|
end
|
|
|
|
end
|