answer.rb 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. class KnowledgeBase::Answer < ApplicationModel
  3. include HasTranslations
  4. include HasAgentAllowedParams
  5. include HasTags
  6. include CanBePublished
  7. include ChecksKbClientNotification
  8. include ChecksKbClientVisibility
  9. include CanCloneAttachments
  10. AGENT_ALLOWED_ATTRIBUTES = %i[category_id promoted internal_note].freeze
  11. AGENT_ALLOWED_NESTED_RELATIONS = %i[translations].freeze
  12. belongs_to :category, class_name: 'KnowledgeBase::Category', inverse_of: :answers, touch: true
  13. scope :include_contents, -> { eager_load(translations: :content) }
  14. scope :sorted, -> { order(position: :asc) }
  15. acts_as_list scope: :category, top_of_list: 0
  16. # provide consistent naming with KB category
  17. alias_attribute :parent, :category
  18. alias assets_essential assets
  19. def attributes_with_association_ids
  20. attrs = super
  21. attrs[:attachments] = attachments_sorted.map { |elem| self.class.attachment_to_hash(elem) }
  22. attrs[:tags] = tag_list
  23. attrs
  24. end
  25. def assets(data = {})
  26. return data if assets_added_to?(data)
  27. data = super(data)
  28. data = category.assets(data)
  29. # include all siblings to make sure ordering is always up to date. Reader gets only accessible siblings.
  30. data = ApplicationModel::CanAssets.reduce(assets_siblings, data)
  31. ApplicationModel::CanAssets.reduce(translations, data)
  32. end
  33. attachments_cleanup!
  34. def attachments_sorted
  35. attachments.sort_by { |elem| elem.filename.downcase }
  36. end
  37. def add_attachment(file)
  38. filename = file.try(:original_filename) || File.basename(file.path)
  39. content_type = file.try(:content_type) || MIME::Types.type_for(filename).first&.content_type || 'application/octet-stream'
  40. Store.create!(
  41. object: self.class.name,
  42. o_id: id,
  43. data: file.read,
  44. filename: filename,
  45. preferences: { 'Content-Type': content_type }
  46. )
  47. touch # rubocop:disable Rails/SkipsModelValidations
  48. translations.each(&:touch)
  49. true
  50. end
  51. def remove_attachment(attachment_id)
  52. attachment = attachments.find { |elem| elem.id == attachment_id.to_i }
  53. raise ActiveRecord::RecordNotFound if attachment.nil?
  54. Store.remove_item(attachment.id)
  55. touch # rubocop:disable Rails/SkipsModelValidations
  56. translations.each(&:touch)
  57. true
  58. end
  59. def api_url
  60. Rails.application.routes.url_helpers.knowledge_base_answer_path(category.knowledge_base, self)
  61. end
  62. # required by CanCloneAttachments
  63. def content_type
  64. 'text/html'
  65. end
  66. private
  67. def assets_siblings(siblings: category.answers, current_user: User.lookup(id: UserInfo.current_user_id))
  68. if KnowledgeBase.granular_permissions?
  69. ep = KnowledgeBase::EffectivePermission.new current_user, category
  70. case ep.access_effective
  71. when 'public_reader'
  72. siblings.published
  73. when 'none'
  74. siblings.none
  75. when 'reader'
  76. siblings.internal
  77. else
  78. siblings
  79. end
  80. elsif !current_user&.permissions?('knowledge_base.editor')
  81. siblings.internal
  82. else
  83. siblings
  84. end
  85. end
  86. def touch_translations
  87. translations.each(&:touch) # move to #touch_all when migrationg to Rails 6
  88. end
  89. after_touch :touch_translations
  90. class << self
  91. def attachment_to_hash(attachment)
  92. url = Rails.application.routes.url_helpers.attachment_path(attachment.id)
  93. {
  94. id: attachment.id,
  95. url: url,
  96. preview_url: "#{url}?preview=1",
  97. filename: attachment.filename,
  98. size: attachment.size,
  99. preferences: attachment.preferences
  100. }
  101. end
  102. end
  103. end