answer.rb 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. # Copyright (C) 2012-2024 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. scope :sorted_by_published, lambda {
  16. reorder(Arel.sql('GREATEST(knowledge_base_answers.published_at, knowledge_base_answers.updated_at) DESC'))
  17. .published
  18. }
  19. scope :sorted_by_internally_published, lambda {
  20. case ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
  21. when 'mysql2'
  22. reorder(Arel.sql('GREATEST(LEAST(IFNULL(knowledge_base_answers.internal_at,1), IFNULL(knowledge_base_answers.published_at, 1)), knowledge_base_answers.updated_at) DESC'))
  23. else
  24. reorder(Arel.sql('GREATEST(LEAST(knowledge_base_answers.internal_at, knowledge_base_answers.published_at), knowledge_base_answers.updated_at) DESC'))
  25. end
  26. .internal
  27. }
  28. acts_as_list scope: :category, top_of_list: 0
  29. # Provide consistent naming with KB category
  30. #
  31. # Originally this used alias_attribute. But alias_attribute for relations for deprecated in Rails 7.1 and removed in 7.2
  32. # However, alias_association was not merged in time for 7.2... So here is a workaround that will hopefully get removed in 7.3!
  33. #
  34. # Related PR: https://github.com/rails/rails/pull/49801
  35. alias parent category
  36. alias parent= category=
  37. alias assets_essential assets
  38. def attributes_with_association_ids
  39. attrs = super
  40. attrs[:attachments] = attachments_sorted.map { |elem| self.class.attachment_to_hash(elem) }
  41. attrs[:tags] = tag_list
  42. attrs
  43. end
  44. def assets(data = {})
  45. return data if assets_added_to?(data)
  46. data = super
  47. data = category.assets(data)
  48. ApplicationModel::CanAssets.reduce(translations, data)
  49. end
  50. attachments_cleanup!
  51. def attachments_sorted
  52. attachments.sort_by { |elem| elem.filename.downcase }
  53. end
  54. def add_attachment(file)
  55. filename = file.try(:original_filename) || File.basename(file.path)
  56. content_type = file.try(:content_type) || MIME::Types.type_for(filename).first&.content_type || 'application/octet-stream'
  57. Store.create!(
  58. object: self.class.name,
  59. o_id: id,
  60. data: file.read,
  61. filename: filename,
  62. preferences: { 'Content-Type': content_type }
  63. )
  64. touch # rubocop:disable Rails/SkipsModelValidations
  65. translations.each(&:touch)
  66. true
  67. end
  68. def remove_attachment(attachment_id)
  69. attachment = attachments.find { |elem| elem.id == attachment_id.to_i }
  70. raise ActiveRecord::RecordNotFound if attachment.nil?
  71. Store.remove_item(attachment.id)
  72. touch # rubocop:disable Rails/SkipsModelValidations
  73. translations.each(&:touch)
  74. true
  75. end
  76. def api_url
  77. Rails.application.routes.url_helpers.knowledge_base_answer_path(category.knowledge_base, self)
  78. end
  79. # required by CanCloneAttachments
  80. def content_type
  81. 'text/html'
  82. end
  83. private
  84. def touch_translations
  85. translations
  86. .reject(&:destroyed?)
  87. .each(&:touch) # touch each translation separately to trigger after_commit callbacks
  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