2021-06-01 12:20:20 +00:00
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
2019-06-04 03:40:48 +00:00
class KnowledgeBase < ApplicationModel
include HasTranslations
include HasAgentAllowedParams
include ChecksKbClientNotification
AGENT_ALLOWED_NESTED_RELATIONS = % i [ translations ] . freeze
LAYOUTS = %w[ grid list ] . freeze
ICONSETS = %w[ FontAwesome anticon material ionicons Simple-Line-Icons ] . freeze
has_many :kb_locales , class_name : 'KnowledgeBase::Locale' ,
inverse_of : :knowledge_base ,
dependent : :destroy
accepts_nested_attributes_for :kb_locales , allow_destroy : true
validates :kb_locales , presence : true
2020-09-30 06:32:13 +00:00
validates :kb_locales , length : { maximum : 1 , message : 'System supports only one locale for knowledge base. Upgrade your plan to use more locales.' } , unless : :multi_lingual_support?
2019-06-04 03:40:48 +00:00
has_many :categories , class_name : 'KnowledgeBase::Category' ,
inverse_of : :knowledge_base ,
dependent : :restrict_with_exception
has_many :answers , through : :categories
validates :category_layout , inclusion : { in : KnowledgeBase :: LAYOUTS }
validates :homepage_layout , inclusion : { in : KnowledgeBase :: LAYOUTS }
2021-09-27 12:28:53 +00:00
validates :color_highlight , presence : true , color : true
validates :color_header , presence : true , color : true
validates :color_header_link , presence : true , color : true
2020-02-19 15:02:28 +00:00
2019-06-04 03:40:48 +00:00
validates :iconset , inclusion : { in : KnowledgeBase :: ICONSETS }
scope :active , - > { where ( active : true ) }
alias assets_essential assets
def assets ( data )
return data if assets_added_to? ( data )
data = super ( data )
ApplicationModel :: CanAssets . reduce ( kb_locales + translations , data )
end
# assets without unnecessary bits
def assets_public ( data )
data = assets_essential ( data )
data [ :KnowledgeBase ] . each do | _ , elem |
elem . delete_if do | k , _ |
2020-05-25 07:05:17 +00:00
k . end_with? ( '_ids' )
2019-06-04 03:40:48 +00:00
end
end
data
end
def custom_address_uri
return nil if custom_address . blank?
2020-02-18 14:36:47 +00:00
scheme = Setting . get ( 'http_type' ) || 'http'
URI ( " #{ scheme } :// #{ custom_address } " )
2019-06-04 03:40:48 +00:00
rescue URI :: InvalidURIError
nil
end
def custom_address_matches? ( request )
uri = custom_address_uri
return false if uri . blank?
given_fqdn = request . headers . env [ 'SERVER_NAME' ] & . downcase
given_path = request . headers . env [ 'HTTP_X_ORIGINAL_URL' ] & . downcase
# original url header not present, server not configured
return false if given_path . nil?
# path doesn't match
return false if uri . path . downcase != given_path [ 0 , uri . path . length ]
# domain present, but doesn't match
return false if uri . host . present? && uri . host . downcase != given_fqdn
true
rescue URI :: InvalidURIError
false
end
2020-08-18 13:01:18 +00:00
def custom_address_prefix ( request )
2020-10-06 18:05:07 +00:00
host = custom_address_uri . host || request . headers . env [ 'SERVER_NAME' ]
port = request . headers . env [ 'SERVER_PORT' ]
2021-09-14 07:46:08 +00:00
port_silent = ( request . ssl? && port == '443' ) || ( ! request . ssl? && port == '80' )
2020-10-06 18:05:07 +00:00
port_string = port_silent ? '' : " : #{ port } "
" #{ custom_address_uri . scheme } :// #{ host } #{ port_string } "
2020-08-18 13:01:18 +00:00
end
2021-03-30 15:56:26 +00:00
def custom_address_path ( path )
uri = custom_address_uri
return path if ! uri
custom_path = custom_address_uri . path || ''
applied_path = path . gsub ( %r{ ^/help } , custom_path )
applied_path . presence || '/'
end
def canonical_host
custom_address_uri & . host || Setting . get ( 'fqdn' )
end
def canonical_scheme_host
" #{ Setting . get ( 'http_type' ) } :// #{ canonical_host } "
end
def canonical_url ( path )
" #{ canonical_scheme_host } #{ custom_address_path ( path ) } "
end
2019-06-04 03:40:48 +00:00
def full_destroy!
ChecksKbClientNotification . disable_in_all_classes!
transaction do
# get all categories with their children and reverse to delete children first
categories . root . map ( & :self_with_children ) . flatten . reverse . each ( & :full_destroy! )
translations . each ( & :destroy! )
kb_locales . each ( & :destroy! )
destroy!
end
ensure
ChecksKbClientNotification . enable_in_all_classes!
end
def visible?
active?
end
def api_url
Rails . application . routes . url_helpers . knowledge_base_path ( self )
end
def load_category ( locale , id )
categories . localed ( locale ) . find_by ( id : id )
end
def self . with_multiple_locales_exists?
KnowledgeBase
. active
. joins ( :kb_locales )
. group ( 'knowledge_bases.id' )
2019-10-28 15:38:14 +00:00
. pluck ( Arel . sql ( 'COUNT(knowledge_base_locales.id) as locales_count' ) )
2019-06-04 03:40:48 +00:00
. any? { | e | e > 1 }
end
private
def set_defaults
self . translations = kb_locales . map do | kb_locale |
name = Setting . get ( 'organization' ) . presence || Setting . get ( 'product_name' ) . presence || 'Zammad'
kb_suffix = :: Translation . translate kb_locale . system_locale . locale , 'Knowledge Base'
KnowledgeBase :: Translation . new (
title : " #{ name } #{ kb_suffix } " ,
footer_note : " © #{ name } " ,
kb_locale : kb_locale
)
end
end
2020-09-30 09:07:01 +00:00
before_validation :patch_custom_address
2019-06-04 03:40:48 +00:00
after_create :set_defaults
def validate_custom_address
return if custom_address . nil?
# not domain, but no leading slash
2020-09-30 09:07:01 +00:00
if custom_address . exclude? ( '.' ) && custom_address [ 0 ] != '/'
2019-06-05 15:44:29 +00:00
errors . add ( :custom_address , 'must begin with a slash ("/").' )
2019-06-04 03:40:48 +00:00
end
if custom_address . include? ( '://' )
2019-06-05 15:44:29 +00:00
errors . add ( :custom_address , 'must not include a protocol (e.g., "http://" or "https://").' )
2019-06-04 03:40:48 +00:00
end
if custom_address . last == '/'
2019-06-05 15:44:29 +00:00
errors . add ( :custom_address , 'must not end with a slash ("/").' )
2019-06-04 03:40:48 +00:00
end
if custom_address == '/' # rubocop:disable Style/GuardClause
errors . add ( :custom_address , 'Please enter valid path or domain' )
end
end
validate :validate_custom_address
def patch_custom_address
self . custom_address = nil if custom_address == ''
end
def multi_lingual_support?
Setting . get 'kb_multi_lingual_support'
end
def set_kb_active_setting
Setting . set 'kb_active' , KnowledgeBase . active . exists?
CanBePublished . update_active_publicly!
end
after_destroy :set_kb_active_setting
2020-09-30 09:07:01 +00:00
after_save :set_kb_active_setting
2019-06-04 03:40:48 +00:00
end