').append(input).html()
+
+ prepareVideos: (input) ->
+ input.replace /\(([\s]*)widget:([\s]*)video[\W]([\s\S])+?\)/g, (match) ->
+ settings = match
+ .slice(1, -1)
+ .split(',')
+ .map (pair) -> pair.split(':').map (elem) -> elem.trim()
+ .reduce (memo, elem) ->
+ memo[elem[0]] = elem[1]
+ return memo
+ , {}
+
+ # coffeelint: disable=indentation
+ url = switch settings.provider
+ when 'youtube'
+ "http://www.youtube.com/embed/#{settings.id}"
+ when 'vimeo'
+ "https://player.vimeo.com/video/#{settings.id}"
+ # coffeelint: enable=indentation
+
+ return match unless url
+
+ ""
renderAttachments: (attachments) ->
@answerAttachments.html App.view('generic/attachments')(
diff --git a/app/assets/javascripts/app/models/knowledge_base_answer.coffee b/app/assets/javascripts/app/models/knowledge_base_answer.coffee
index 6f71e854d..ba67007d4 100644
--- a/app/assets/javascripts/app/models/knowledge_base_answer.coffee
+++ b/app/assets/javascripts/app/models/knowledge_base_answer.coffee
@@ -64,6 +64,8 @@ class App.KnowledgeBaseAnswer extends App.Model
buttons: [
'link'
'link_answer'
+ 'insert_image'
+ 'embed_video'
]
display: 'Content'
tag: 'richtext'
diff --git a/app/assets/stylesheets/knowledge_base.scss b/app/assets/stylesheets/knowledge_base.scss
index aa81412a1..36b43ac6d 100644
--- a/app/assets/stylesheets/knowledge_base.scss
+++ b/app/assets/stylesheets/knowledge_base.scss
@@ -1233,3 +1233,18 @@ b {
@extend %clickable;
}
}
+
+.videoWrapper {
+ position: relative;
+ padding-bottom: 56.25%; /* 16:9 */
+ padding-top: 25px;
+ height: 0;
+}
+
+.videoWrapper iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss
index 394ba381e..35579edfe 100644
--- a/app/assets/stylesheets/zammad.scss
+++ b/app/assets/stylesheets/zammad.scss
@@ -11622,3 +11622,19 @@ span.is-disabled {
.highlight-emulator {
background-color: highlight;
}
+
+
+.videoWrapper {
+ position: relative;
+ padding-bottom: 56.25%; /* 16:9 */
+ padding-top: 25px;
+ height: 0;
+}
+
+.videoWrapper iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
diff --git a/app/helpers/knowledge_base_rich_text_helper.rb b/app/helpers/knowledge_base_rich_text_helper.rb
index 2589f3efe..cb945ee36 100644
--- a/app/helpers/knowledge_base_rich_text_helper.rb
+++ b/app/helpers/knowledge_base_rich_text_helper.rb
@@ -1,4 +1,8 @@
module KnowledgeBaseRichTextHelper
+ def prepare_rich_text(input)
+ prepare_rich_text_videos(prepare_rich_text_links(input))
+ end
+
def prepare_rich_text_links(input)
scrubber = Loofah::Scrubber.new do |node|
next if node.name != 'a'
@@ -22,4 +26,26 @@ module KnowledgeBaseRichTextHelper
parsed
end
+
+ def prepare_rich_text_videos(input)
+ input.gsub(/\(([\s]*)widget:([\s]*)video[\W]([\s\S])+?\)/) do |match|
+ settings = match
+ .slice(1...-1)
+ .split(',')
+ .map { |pair| pair.split(':').map(&:strip) }
+ .to_h
+ .symbolize_keys
+
+ url = case settings[:provider]
+ when 'youtube'
+ "http://www.youtube.com/embed/#{settings[:id]}"
+ when 'vimeo'
+ "https://player.vimeo.com/video/#{settings[:id]}"
+ end
+
+ return match unless url
+
+ ""
+ end
+ end
end
diff --git a/app/models/concerns/has_rich_text.rb b/app/models/concerns/has_rich_text.rb
index a66a9dc57..b9d6b0afb 100644
--- a/app/models/concerns/has_rich_text.rb
+++ b/app/models/concerns/has_rich_text.rb
@@ -50,6 +50,21 @@ Checks if file is used inline
parsed = Loofah.scrub_fragment(raw, scrubber).to_s
parsed = HtmlSanitizer.strict(parsed)
+ scrubber_cleaner = Loofah::Scrubber.new(direction: :bottom_up) do |node|
+ case node.name
+ when 'span'
+ node.children.reject { |t| ["\n", "\r", "\r\n"].include?(t.text) }.each { |child| node.before child }
+
+ node.remove
+ when 'div'
+ node.children.to_a.select { |t| t.text.match?(/\A([\n\r]+)\z/) }.each(&:remove)
+
+ node.remove if node.children.none? && node.classes.none?
+ end
+ end
+
+ parsed = Loofah.scrub_fragment(parsed, scrubber_cleaner).to_s
+
(parsed, attachments_inline) = HtmlSanitizer.replace_inline_images(parsed, image_prefix)
send("#{attr}=", parsed)
diff --git a/app/views/knowledge_base/public/answers/show.html.erb b/app/views/knowledge_base/public/answers/show.html.erb
index 86c0f9328..2c57cf04b 100644
--- a/app/views/knowledge_base/public/answers/show.html.erb
+++ b/app/views/knowledge_base/public/answers/show.html.erb
@@ -8,7 +8,7 @@ data-available-locales='<%= @object_locales.map(&:locale).join(',') %>'>
- <%= prepare_rich_text_links @object.translation.content.body_with_urls.html_safe %>
+ <%= prepare_rich_text(@object.translation.content.body_with_urls).html_safe %>
<% if (attachments = @object.attachments_sorted) && attachments.present? %>
diff --git a/spec/factories/knowledge_base/answer.rb b/spec/factories/knowledge_base/answer.rb
index 55d4d3f57..8f5789bdd 100644
--- a/spec/factories/knowledge_base/answer.rb
+++ b/spec/factories/knowledge_base/answer.rb
@@ -2,14 +2,21 @@ FactoryBot.define do
factory 'knowledge_base/answer', aliases: %i[knowledge_base_answer] do
transient do
add_translation { true }
+ translation_traits { [] }
end
category { create(:knowledge_base_category) }
- before(:create) do |answer|
+ before(:create) do |answer, context|
next if answer.translations.present?
- answer.translations << build('knowledge_base/answer/translation', answer: answer)
+ answer.translations << build('knowledge_base/answer/translation', *context.translation_traits, answer: answer)
+ end
+
+ trait :with_video do
+ transient do
+ translation_traits { [:with_video] }
+ end
end
end
end
diff --git a/spec/factories/knowledge_base/answer/translation.rb b/spec/factories/knowledge_base/answer/translation.rb
index 980971b57..1fe545004 100644
--- a/spec/factories/knowledge_base/answer/translation.rb
+++ b/spec/factories/knowledge_base/answer/translation.rb
@@ -24,5 +24,9 @@ FactoryBot.define do
translation.kb_locale = translation.answer.category.knowledge_base.kb_locales.first
end
end
+
+ trait :with_video do
+ content { build(:knowledge_base_answer_translation_content, :with_video) }
+ end
end
end
diff --git a/spec/factories/knowledge_base/answer/translation/content.rb b/spec/factories/knowledge_base/answer/translation/content.rb
index 946e9f5da..c9d9b1f22 100644
--- a/spec/factories/knowledge_base/answer/translation/content.rb
+++ b/spec/factories/knowledge_base/answer/translation/content.rb
@@ -8,5 +8,9 @@ FactoryBot.define do
create(:knowledge_base_answer_translation, content: content)
end
end
+
+ trait :with_video do
+ body { '( widget: video, provider: youtube, id: vTTzwJsHpU8 )' }
+ end
end
end
diff --git a/spec/support/knowledge_base_contexts.rb b/spec/support/knowledge_base_contexts.rb
index 8350b81f1..d6ff5fe08 100644
--- a/spec/support/knowledge_base_contexts.rb
+++ b/spec/support/knowledge_base_contexts.rb
@@ -23,6 +23,10 @@ RSpec.shared_context 'basic Knowledge Base', current_user_id: 1 do
create(:knowledge_base_answer, category: category, published_at: 1.week.ago)
end
+ let :published_answer_with_video do
+ create(:knowledge_base_answer, :with_video, category: category, published_at: 1.week.ago)
+ end
+
let :internal_answer do
create(:knowledge_base_answer, category: category, internal_at: 1.week.ago)
end
diff --git a/spec/system/knowledge_base/locale/answer/edit_spec.rb b/spec/system/knowledge_base/locale/answer/edit_spec.rb
index 5a8d874d3..bc656d416 100644
--- a/spec/system/knowledge_base/locale/answer/edit_spec.rb
+++ b/spec/system/knowledge_base/locale/answer/edit_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Knowledge Base Locale Answer Edit', type: :system, authenticated
published_answer && draft_answer && internal_answer
end
- it 'when entering a long text' do
+ it 'wraps long texts' do
long_string = '3KKFA9DAWE9VJYNNnpYRRtMwfa168O1yvpD2t9QXsfb3cppGV6KZ12q0UUJIy5r4Exfk18GnWPR0A3SoDsjxIHz1Gcu4aCEVzenilSOu4gAfxnB6k3mSBUOGIfdgChEBYhcHGgiCmV2EoXu4gG7GAJxKJhM2d4NUiL5RZttGtMXYYFr2Jsg7MV7xXGcygnsLMYqnwzOJxBK0vH3fzhdIZd6YrqR3fggaY0RyKtVigOBZ2SETC8s238Z9eDL4gfUW'
visit "#knowledge_base/#{knowledge_base.id}/locale/#{primary_locale.system_locale.locale}/answer/#{draft_answer.id}/edit"
@@ -19,4 +19,31 @@ RSpec.describe 'Knowledge Base Locale Answer Edit', type: :system, authenticated
expect(page).to have_css('.page-header-title') { |elem| !elem.obscured? }
end
end
+
+ context 'embedded video' do
+
+ it 'has adding functionality' do
+ visit "#knowledge_base/#{knowledge_base.id}/locale/#{primary_locale.system_locale.locale}/answer/#{published_answer.id}/edit"
+
+ sleep 3 # wait for popover killer to pass
+
+ find('a[data-action="embed_video"]').click
+
+ within('.popover-content') do
+ find('input').fill_in with: 'https://www.youtube.com/watch?v=vTTzwJsHpU8'
+ find('[type=submit]').click
+ end
+
+ within('.richtext-content') do
+ expect(page).to have_text('( widget: video, provider: youtube, id: vTTzwJsHpU8 )')
+ end
+ end
+
+ it 'loads stored' do
+ visit "#knowledge_base/#{knowledge_base.id}/locale/#{primary_locale.system_locale.locale}/answer/#{published_answer_with_video.id}"
+
+ iframe = find('iframe')
+ expect(iframe['src']).to start_with('http://www.youtube.com/embed/')
+ end
+ end
end
diff --git a/spec/system/knowledge_base_public/answer_spec.rb b/spec/system/knowledge_base_public/answer_spec.rb
new file mode 100644
index 000000000..e5e5532b1
--- /dev/null
+++ b/spec/system/knowledge_base_public/answer_spec.rb
@@ -0,0 +1,19 @@
+require 'rails_helper'
+
+RSpec.describe 'Public Knowledge Base answer', type: :system, authenticated: false do
+ include_context 'basic Knowledge Base'
+
+ context 'video content' do
+
+ before do
+ published_answer_with_video
+ end
+
+ it 'shows video player' do
+ visit help_answer_path(primary_locale.system_locale.locale, category, published_answer_with_video)
+
+ iframe = find('iframe')
+ expect(iframe['src']).to start_with('http://www.youtube.com/embed/')
+ end
+ end
+end