category.rb 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. class KnowledgeBase::Category < ApplicationModel
  3. include HasTranslations
  4. include HasAgentAllowedParams
  5. include ChecksKbClientNotification
  6. include ChecksKbClientVisibility
  7. AGENT_ALLOWED_ATTRIBUTES = %i[knowledge_base_id parent_id category_icon].freeze
  8. AGENT_ALLOWED_NESTED_RELATIONS = %i[translations].freeze
  9. belongs_to :knowledge_base, inverse_of: :categories
  10. has_many :answers, class_name: 'KnowledgeBase::Answer',
  11. inverse_of: :category,
  12. dependent: :restrict_with_exception
  13. has_many :children, class_name: 'KnowledgeBase::Category',
  14. foreign_key: :parent_id,
  15. inverse_of: :parent,
  16. dependent: :restrict_with_exception
  17. belongs_to :parent, class_name: 'KnowledgeBase::Category',
  18. inverse_of: :children,
  19. touch: true,
  20. optional: true
  21. has_many :permissions, class_name: 'KnowledgeBase::Permission',
  22. as: :permissionable,
  23. autosave: true,
  24. dependent: :destroy
  25. validates :category_icon, presence: true
  26. scope :root, -> { where(parent: nil) }
  27. scope :sorted, -> { order(position: :asc) }
  28. acts_as_list scope: :parent, top_of_list: 0
  29. alias assets_essential assets
  30. def assets(data = {})
  31. return data if assets_added_to?(data)
  32. data = super(data)
  33. data = knowledge_base.assets(data)
  34. # include all siblings to make sure ordering is always up to date
  35. data = ApplicationModel::CanAssets.reduce(assets_siblings, data)
  36. data = ApplicationModel::CanAssets.reduce(translations, data)
  37. # include parent category or KB for root to have full path
  38. (parent || knowledge_base).assets(data)
  39. end
  40. def self_parent?(candidate)
  41. return true if candidate == parent
  42. return true if parent&.self_parent?(candidate)
  43. end
  44. def self_with_children
  45. [self] + children.map(&:self_with_children).flatten
  46. end
  47. def self_with_parents
  48. result = [self]
  49. check = self
  50. while check.parent.present?
  51. result << check.parent
  52. check = check.parent
  53. end
  54. result
  55. end
  56. def self_with_children_answers
  57. KnowledgeBase::Answer.where(category_id: self_with_children_ids)
  58. end
  59. def self_with_children_ids
  60. output = [id]
  61. output << KnowledgeBase::Category.where(parent_id: output.last).pluck(:id) while output.last.present?
  62. output.flatten
  63. end
  64. def full_destroy!
  65. transaction do
  66. answers.each(&:destroy!)
  67. answers.reset
  68. children.reset
  69. destroy!
  70. end
  71. end
  72. def public_content?(kb_locale = nil)
  73. scope = self_with_children_answers.published
  74. scope = scope.localed(kb_locale.system_locale) if kb_locale
  75. scope.any?
  76. end
  77. def internal_content?(kb_locale = nil)
  78. scope = self_with_children_answers.internal
  79. scope = scope.localed(kb_locale.system_locale) if kb_locale
  80. scope.any?
  81. end
  82. def visible?(kb_locale = nil)
  83. public_content?(kb_locale)
  84. end
  85. def api_url
  86. Rails.application.routes.url_helpers.knowledge_base_category_path(knowledge_base, self)
  87. end
  88. def permissions_effective
  89. cache_key = KnowledgeBase::Permission.cache_key self
  90. Rails.cache.fetch cache_key do
  91. KnowledgeBase::Category::Permission.new(self).permissions_effective
  92. end
  93. end
  94. def attributes_with_association_ids
  95. attrs = super
  96. attrs[:permissions_effective] = permissions_effective
  97. attrs
  98. end
  99. private
  100. def assets_siblings(siblings: sibling_categories, current_user: User.lookup(id: UserInfo.current_user_id))
  101. granular = KnowledgeBase.granular_permissions?
  102. if !granular && !current_user&.permissions?('knowledge_base.editor')
  103. siblings.select(&:internal_content?)
  104. elsif granular
  105. siblings.select { |elem| assets_siblings_applicable?(elem, current_user) }
  106. else
  107. siblings
  108. end
  109. end
  110. def assets_siblings_applicable?(elem, current_user)
  111. ep = KnowledgeBase::EffectivePermission.new(current_user, elem)
  112. case ep.access_effective
  113. when 'none'
  114. false
  115. when 'reader'
  116. elem.internal_content?
  117. else
  118. true
  119. end
  120. end
  121. def cannot_be_child_of_parent
  122. errors.add(:parent_id, 'cannot be a child of the parent') if self_parent?(self)
  123. end
  124. validate :cannot_be_child_of_parent
  125. def sibling_categories
  126. parent&.children || knowledge_base.categories.root
  127. end
  128. end