Commit 0eccee7e authored by Robert Speicher's avatar Robert Speicher

Merge branch '320976-jira-issue-image-links-with-paperclip-icon' into 'master'

Jira Issue Detail page: Jira private links with paperclip icons

See merge request gitlab-org/gitlab!54821
parents b0bed714 699ca326
...@@ -430,8 +430,7 @@ ...@@ -430,8 +430,7 @@
} }
} }
a[href*='/uploads/'], a.with-attachment-icon {
a[href*='storage.googleapis.com/google-code-attachments/'] {
&::before { &::before {
margin-right: 4px; margin-right: 4px;
...@@ -441,6 +440,11 @@ ...@@ -441,6 +440,11 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
content: '📎'; content: '📎';
} }
}
a[href*='/uploads/'],
a[href*='storage.googleapis.com/google-code-attachments/'] {
@extend .with-attachment-icon;
&.no-attachment-icon { &.no-attachment-icon {
&::before { &::before {
......
...@@ -5,13 +5,14 @@ module Banzai ...@@ -5,13 +5,14 @@ module Banzai
# HTML filter that replaces the Jira private images with the link to the image. # HTML filter that replaces the Jira private images with the link to the image.
class JiraPrivateImageLinkFilter < HTML::Pipeline::Filter class JiraPrivateImageLinkFilter < HTML::Pipeline::Filter
PRIVATE_IMAGE_PATH = '/secure/attachment/' PRIVATE_IMAGE_PATH = '/secure/attachment/'
CSS_WITH_ATTACHMENT_ICON = 'with-attachment-icon'
def call def call
doc.xpath('descendant-or-self::img').each do |img| doc.xpath('descendant-or-self::img').each do |img|
next unless img['src'].start_with?(PRIVATE_IMAGE_PATH) next unless img['src'].start_with?(PRIVATE_IMAGE_PATH)
img_link = "#{project.jira_service.url}#{img['src']}" img_link = "#{project.jira_service.url}#{img['src']}"
link = "<a href=\"#{img_link}\">#{img_link}</a>" link = "<a class=\"#{CSS_WITH_ATTACHMENT_ICON}\" href=\"#{img_link}\">#{img_link}</a>"
img.replace(link) img.replace(link)
end end
......
# frozen_string_literal: true
module EE
module Banzai
module Filter
module SanitizationFilter
extend ::Gitlab::Utils::Override
extend ActiveSupport::Concern
override :customize_allowlist
def customize_allowlist(allowlist)
# Remove any `class` property not required for a
allowlist[:attributes]['a'].push('class')
allowlist[:transformers].push(self.class.remove_unsafe_a_class)
super(allowlist)
end
class_methods do
def remove_unsafe_a_class
lambda do |env|
node = env[:node]
return unless node.name == 'a'
return unless node.has_attribute?('class')
return if node['class'] == ::Banzai::Filter::JiraPrivateImageLinkFilter::CSS_WITH_ATTACHMENT_ICON
node.remove_attribute('class')
end
end
end
end
end
end
end
...@@ -15,7 +15,7 @@ RSpec.describe Banzai::Filter::JiraPrivateImageLinkFilter do ...@@ -15,7 +15,7 @@ RSpec.describe Banzai::Filter::JiraPrivateImageLinkFilter do
it 'replaces the Jira private images with the link to the image' do it 'replaces the Jira private images with the link to the image' do
doc = filter("<img src=\"#{img_link}\">", context) doc = filter("<img src=\"#{img_link}\">", context)
expect(doc.to_html).to eq("<a href=\"#{jira_service.url}#{img_link}\">#{jira_service.url}#{img_link}</a>") expect(doc.to_html).to eq("<a class=\"with-attachment-icon\" href=\"#{jira_service.url}#{img_link}\">#{jira_service.url}#{img_link}</a>")
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Banzai::Filter::SanitizationFilter do
include FilterSpecHelper
describe 'custom allowlist' do
it 'sanitizes `class` attribute from a' do
act = '<a class="k" href="http://example.com/url">Link</a>'
expect(filter(act).to_html).to eq('<a href="http://example.com/url">Link</a>')
end
it 'allows `with-attachment-icon` class in `a` elements' do
html = '<a class="with-attachment-icon" href="http://example.com/jira.png">http://example.com/jira.png</a>'
doc = filter(html)
expect(doc.at_css('a')['class']).to eq('with-attachment-icon')
end
end
end
...@@ -64,3 +64,5 @@ module Banzai ...@@ -64,3 +64,5 @@ module Banzai
end end
end end
end end
Banzai::Filter::SanitizationFilter.prepend_if_ee('EE::Banzai::Filter::SanitizationFilter')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment