knowledge_base.rb 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class KnowledgeBase < ApplicationModel
  3. include HasTranslations
  4. include HasAgentAllowedParams
  5. include ChecksKbClientNotification
  6. AGENT_ALLOWED_NESTED_RELATIONS = %i[translations].freeze
  7. LAYOUTS = %w[grid list].freeze
  8. ICONSETS = %w[FontAwesome anticon material ionicons Simple-Line-Icons].freeze
  9. has_many :kb_locales, class_name: 'KnowledgeBase::Locale',
  10. inverse_of: :knowledge_base,
  11. dependent: :destroy
  12. accepts_nested_attributes_for :kb_locales, allow_destroy: true
  13. validates :kb_locales, presence: true
  14. validates :kb_locales, length: { maximum: 1 }, unless: :multi_lingual_support?
  15. has_many :categories, class_name: 'KnowledgeBase::Category',
  16. inverse_of: :knowledge_base,
  17. dependent: :restrict_with_exception
  18. has_many :answers, through: :categories
  19. validates :category_layout, inclusion: { in: KnowledgeBase::LAYOUTS }
  20. validates :homepage_layout, inclusion: { in: KnowledgeBase::LAYOUTS }
  21. validates :color_highlight, presence: true
  22. validates :color_header, presence: true
  23. validates :iconset, inclusion: { in: KnowledgeBase::ICONSETS }
  24. scope :active, -> { where(active: true) }
  25. scope :check_active_unless_editor, lambda { |user|
  26. return if user&.permissions? 'knowledge_base.editor'
  27. active
  28. }
  29. alias assets_essential assets
  30. def assets(data)
  31. return data if assets_added_to?(data)
  32. data = super(data)
  33. ApplicationModel::CanAssets.reduce(kb_locales + translations, data)
  34. end
  35. # assets without unnecessary bits
  36. def assets_public(data)
  37. data = assets_essential(data)
  38. data[:KnowledgeBase].each do |_, elem|
  39. elem.delete_if do |k, _|
  40. k.match?(/_ids$/)
  41. end
  42. end
  43. data
  44. end
  45. def custom_address_uri
  46. return nil if custom_address.blank?
  47. URI("protocol://#{custom_address}")
  48. rescue URI::InvalidURIError
  49. nil
  50. end
  51. def custom_address_matches?(request)
  52. uri = custom_address_uri
  53. return false if uri.blank?
  54. given_fqdn = request.headers.env['SERVER_NAME']&.downcase
  55. given_path = request.headers.env['HTTP_X_ORIGINAL_URL']&.downcase
  56. # original url header not present, server not configured
  57. return false if given_path.nil?
  58. # path doesn't match
  59. return false if uri.path.downcase != given_path[0, uri.path.length]
  60. # domain present, but doesn't match
  61. return false if uri.host.present? && uri.host.downcase != given_fqdn
  62. true
  63. rescue URI::InvalidURIError
  64. false
  65. end
  66. def full_destroy!
  67. ChecksKbClientNotification.disable_in_all_classes!
  68. transaction do
  69. # get all categories with their children and reverse to delete children first
  70. categories.root.map(&:self_with_children).flatten.reverse.each(&:full_destroy!)
  71. translations.each(&:destroy!)
  72. kb_locales.each(&:destroy!)
  73. destroy!
  74. end
  75. ensure
  76. ChecksKbClientNotification.enable_in_all_classes!
  77. end
  78. def visible?
  79. active?
  80. end
  81. def api_url
  82. Rails.application.routes.url_helpers.knowledge_base_path(self)
  83. end
  84. def load_category(locale, id)
  85. categories.localed(locale).find_by(id: id)
  86. end
  87. def self.with_multiple_locales_exists?
  88. KnowledgeBase
  89. .active
  90. .joins(:kb_locales)
  91. .group('knowledge_bases.id')
  92. .pluck('COUNT(knowledge_base_locales.id) as locales_count')
  93. .any? { |e| e > 1 }
  94. end
  95. private
  96. def set_defaults
  97. self.translations = kb_locales.map do |kb_locale|
  98. name = Setting.get('organization').presence || Setting.get('product_name').presence || 'Zammad'
  99. kb_suffix = ::Translation.translate kb_locale.system_locale.locale, 'Knowledge Base'
  100. KnowledgeBase::Translation.new(
  101. title: "#{name} #{kb_suffix}",
  102. footer_note: "© #{name}",
  103. kb_locale: kb_locale
  104. )
  105. end
  106. end
  107. after_create :set_defaults
  108. def validate_custom_address
  109. return if custom_address.nil?
  110. # not domain, but no leading slash
  111. if !custom_address.include?('.') && custom_address[0] != '/'
  112. errors.add(:custom_address, 'must begin with a slash ("/").')
  113. end
  114. if custom_address.include?('://')
  115. errors.add(:custom_address, 'must not include a protocol (e.g., "http://" or "https://").')
  116. end
  117. if custom_address.last == '/'
  118. errors.add(:custom_address, 'must not end with a slash ("/").')
  119. end
  120. if custom_address == '/' # rubocop:disable Style/GuardClause
  121. errors.add(:custom_address, 'Please enter valid path or domain')
  122. end
  123. end
  124. validate :validate_custom_address
  125. def patch_custom_address
  126. self.custom_address = nil if custom_address == ''
  127. end
  128. before_validation :patch_custom_address
  129. def multi_lingual_support?
  130. Setting.get 'kb_multi_lingual_support'
  131. end
  132. def set_kb_active_setting
  133. Setting.set 'kb_active', KnowledgeBase.active.exists?
  134. CanBePublished.update_active_publicly!
  135. end
  136. after_save :set_kb_active_setting
  137. after_destroy :set_kb_active_setting
  138. end