123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class KnowledgeBase::Answer < ApplicationModel
- include HasTranslations
- include HasAgentAllowedParams
- include HasTags
- include CanBePublished
- include ChecksKbClientNotification
- include ChecksKbClientVisibility
- include CanCloneAttachments
- AGENT_ALLOWED_ATTRIBUTES = %i[category_id promoted internal_note].freeze
- AGENT_ALLOWED_NESTED_RELATIONS = %i[translations].freeze
- belongs_to :category, class_name: 'KnowledgeBase::Category', inverse_of: :answers, touch: true
- scope :include_contents, -> { eager_load(translations: :content) }
- scope :sorted, -> { order(position: :asc) }
- scope :sorted_by_published, lambda {
- reorder(Arel.sql('GREATEST(knowledge_base_answers.published_at, knowledge_base_answers.updated_at) DESC'))
- .published
- }
- scope :sorted_by_internally_published, lambda {
- case ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
- when 'mysql2'
- 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'))
- else
- reorder(Arel.sql('GREATEST(LEAST(knowledge_base_answers.internal_at, knowledge_base_answers.published_at), knowledge_base_answers.updated_at) DESC'))
- end
- .internal
- }
- acts_as_list scope: :category, top_of_list: 0
- # provide consistent naming with KB category
- alias_attribute :parent, :category
- alias assets_essential assets
- def attributes_with_association_ids
- attrs = super
- attrs[:attachments] = attachments_sorted.map { |elem| self.class.attachment_to_hash(elem) }
- attrs[:tags] = tag_list
- attrs
- end
- def assets(data = {})
- return data if assets_added_to?(data)
- data = super(data)
- data = category.assets(data)
- ApplicationModel::CanAssets.reduce(translations, data)
- end
- attachments_cleanup!
- def attachments_sorted
- attachments.sort_by { |elem| elem.filename.downcase }
- end
- def add_attachment(file)
- filename = file.try(:original_filename) || File.basename(file.path)
- content_type = file.try(:content_type) || MIME::Types.type_for(filename).first&.content_type || 'application/octet-stream'
- Store.create!(
- object: self.class.name,
- o_id: id,
- data: file.read,
- filename: filename,
- preferences: { 'Content-Type': content_type }
- )
- touch # rubocop:disable Rails/SkipsModelValidations
- translations.each(&:touch)
- true
- end
- def remove_attachment(attachment_id)
- attachment = attachments.find { |elem| elem.id == attachment_id.to_i }
- raise ActiveRecord::RecordNotFound if attachment.nil?
- Store.remove_item(attachment.id)
- touch # rubocop:disable Rails/SkipsModelValidations
- translations.each(&:touch)
- true
- end
- def api_url
- Rails.application.routes.url_helpers.knowledge_base_answer_path(category.knowledge_base, self)
- end
- # required by CanCloneAttachments
- def content_type
- 'text/html'
- end
- private
- def touch_translations
- translations
- .reject(&:destroyed?)
- .each(&:touch) # touch each translation separately to trigger after_commit callbacks
- end
- after_touch :touch_translations
- class << self
- def attachment_to_hash(attachment)
- url = Rails.application.routes.url_helpers.attachment_path(attachment.id)
- {
- id: attachment.id,
- url: url,
- preview_url: "#{url}?preview=1",
- filename: attachment.filename,
- size: attachment.size,
- preferences: attachment.preferences
- }
- end
- end
- end
|