diff --git a/lib/chatwoot_markdown_renderer.rb b/lib/chatwoot_markdown_renderer.rb index 5fa8998b3..2d837f8b7 100644 --- a/lib/chatwoot_markdown_renderer.rb +++ b/lib/chatwoot_markdown_renderer.rb @@ -9,9 +9,9 @@ class ChatwootMarkdownRenderer end def render_article - superscript_renderer = SuperscriptRenderer.new + markdown_renderer = CustomMarkdownRenderer.new doc = CommonMarker.render_doc(@content, :DEFAULT) - html = superscript_renderer.render(doc) + html = markdown_renderer.render(doc) render_as_html_safe(html) end diff --git a/lib/custom_markdown_renderer.rb b/lib/custom_markdown_renderer.rb new file mode 100644 index 000000000..b683c9281 --- /dev/null +++ b/lib/custom_markdown_renderer.rb @@ -0,0 +1,93 @@ +class CustomMarkdownRenderer < CommonMarker::HtmlRenderer + YOUTUBE_REGEX = %r{https?://(?:www\.)?(?:youtube\.com/watch\?v=|youtu\.be/)([^&/]+)} + VIMEO_REGEX = %r{https?://(?:www\.)?vimeo\.com/(\d+)} + MP4_REGEX = %r{https?://(?:www\.)?.+\.(mp4)} + + def text(node) + content = node.string_content + + if content.include?('^') + split_content = parse_sup(content) + out(split_content.join) + else + out(escape_html(content)) + end + end + + def link(node) + render_embedded_content(node) || super + end + + private + + def render_embedded_content(node) + link_url = node.url + + youtube_match = link_url.match(YOUTUBE_REGEX) + if youtube_match + out(make_youtube_embed(youtube_match)) + return true + end + + vimeo_match = link_url.match(VIMEO_REGEX) + if vimeo_match + out(make_vimeo_embed(vimeo_match)) + return true + end + + mp4_match = link_url.match(MP4_REGEX) + if mp4_match + out(make_video_embed(link_url)) + return true + end + + false + end + + def parse_sup(content) + content.split(/(\^[^\^]+\^)/).map do |segment| + if segment.start_with?('^') && segment.end_with?('^') + "#{escape_html(segment[1..-2])}" + else + escape_html(segment) + end + end + end + + def make_youtube_embed(youtube_match) + video_id = youtube_match[1] + %( + + ) + end + + def make_vimeo_embed(vimeo_match) + video_id = vimeo_match[1] + %( + + ) + end + + def make_video_embed(link_url) + %( + + ) + end +end diff --git a/lib/superscript_renderer.rb b/lib/superscript_renderer.rb deleted file mode 100644 index 485ffd1f0..000000000 --- a/lib/superscript_renderer.rb +++ /dev/null @@ -1,28 +0,0 @@ -class SuperscriptRenderer < CommonMarker::HtmlRenderer - def text(node) - content = node.string_content - - # Check for presence of '^' in the content - if content.include?('^') - # Split the text and insert tags where necessary - split_content = parse_sup(content) - # Output the transformed content - out(split_content.join) - else - # Output the original content - out(escape_html(content)) - end - end - - private - - def parse_sup(content) - content.split(/(\^[^\^]+\^)/).map do |segment| - if segment.start_with?('^') && segment.end_with?('^') - "#{escape_html(segment[1..-2])}" - else - escape_html(segment) - end - end - end -end diff --git a/spec/lib/chatwoot_markdown_renderer_spec.rb b/spec/lib/chatwoot_markdown_renderer_spec.rb index 74e6777c2..dbe9b8245 100644 --- a/spec/lib/chatwoot_markdown_renderer_spec.rb +++ b/spec/lib/chatwoot_markdown_renderer_spec.rb @@ -4,13 +4,13 @@ RSpec.describe ChatwootMarkdownRenderer do let(:markdown_content) { 'This is a *test* content with ^markdown^' } let(:doc) { instance_double(CommonMarker::Node) } let(:renderer) { described_class.new(markdown_content) } - let(:superscript_renderer) { instance_double(SuperscriptRenderer) } + let(:markdown_renderer) { instance_double(CustomMarkdownRenderer) } let(:html_content) { '

This is a test content with markdown

' } before do allow(CommonMarker).to receive(:render_doc).with(markdown_content, :DEFAULT).and_return(doc) - allow(SuperscriptRenderer).to receive(:new).and_return(superscript_renderer) - allow(superscript_renderer).to receive(:render).with(doc).and_return(html_content) + allow(CustomMarkdownRenderer).to receive(:new).and_return(markdown_renderer) + allow(markdown_renderer).to receive(:render).with(doc).and_return(html_content) end describe '#render_article' do diff --git a/spec/lib/custom_markdown_renderer_spec.rb b/spec/lib/custom_markdown_renderer_spec.rb new file mode 100644 index 000000000..623f12de8 --- /dev/null +++ b/spec/lib/custom_markdown_renderer_spec.rb @@ -0,0 +1,117 @@ +require 'rails_helper' + +describe CustomMarkdownRenderer do + let(:renderer) { described_class.new } + + def render_markdown(markdown) + doc = CommonMarker.render_doc(markdown, :DEFAULT) + renderer.render(doc) + end + + describe '#text' do + it 'converts text wrapped in ^ to superscript' do + markdown = 'This is an example of a superscript: ^superscript^.' + expect(render_markdown(markdown)).to include('superscript') + end + + it 'does not convert text not wrapped in ^' do + markdown = 'This is an example without superscript.' + expect(render_markdown(markdown)).not_to include('') + end + + it 'converts multiple superscripts in the same text' do + markdown = 'This is an example with ^multiple^ ^superscripts^.' + rendered_html = render_markdown(markdown) + expect(rendered_html.scan('').length).to eq(2) + expect(rendered_html).to include('multiple') + expect(rendered_html).to include('superscripts') + end + end + + describe 'broken ^ usage' do + it 'does not convert text that only starts with ^' do + markdown = 'This is an example with ^broken superscript.' + expected_output = '

This is an example with ^broken superscript.

' + expect(render_markdown(markdown)).to include(expected_output) + end + + it 'does not convert text that only ends with ^' do + markdown = 'This is an example with broken^ superscript.' + expected_output = '

This is an example with broken^ superscript.

' + expect(render_markdown(markdown)).to include(expected_output) + end + + it 'does not convert text with uneven numbers of ^' do + markdown = 'This is an example with ^broken^ superscript^.' + expected_output = '

This is an example with broken superscript^.

' + expect(render_markdown(markdown)).to include(expected_output) + end + end + + describe '#link' do + def render_markdown_link(link) + doc = CommonMarker.render_doc("[link](#{link})", :DEFAULT) + renderer.render(doc) + end + + context 'when link is a YouTube URL' do + let(:youtube_url) { 'https://www.youtube.com/watch?v=VIDEO_ID' } + + it 'renders an iframe with YouTube embed code' do + output = render_markdown_link(youtube_url) + expect(output).to include(` +