answer.rb 3.4 KB

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