Improved external attachment support.
This commit is contained in:
parent
b09ecbe23e
commit
14507180a3
9 changed files with 170 additions and 26 deletions
|
@ -0,0 +1,19 @@
|
||||||
|
class App.TicketZoomArticleImageView extends App.ControllerModal
|
||||||
|
buttonClose: true
|
||||||
|
buttonCancel: true
|
||||||
|
buttonSubmit: 'Download'
|
||||||
|
buttonClass: 'btn--success'
|
||||||
|
head: ''
|
||||||
|
large: true
|
||||||
|
|
||||||
|
events:
|
||||||
|
'submit form': 'submit'
|
||||||
|
'click .js-cancel': 'cancel'
|
||||||
|
'click .js-close': 'cancel'
|
||||||
|
|
||||||
|
content: ->
|
||||||
|
"<div class=\"centered\">#{@image}</div>"
|
||||||
|
|
||||||
|
onSubmit: =>
|
||||||
|
url = "#{$(@image).attr('src')}?disposition=attachment"
|
||||||
|
window.open(url, '_blank')
|
|
@ -53,6 +53,7 @@ class ArticleViewItem extends App.ObserverController
|
||||||
'click .textBubble': 'toggleMetaWithDelay'
|
'click .textBubble': 'toggleMetaWithDelay'
|
||||||
'click .textBubble a': 'stopPropagation'
|
'click .textBubble a': 'stopPropagation'
|
||||||
'click .js-toggleFold': 'toggleFold'
|
'click .js-toggleFold': 'toggleFold'
|
||||||
|
'click .richtext-content img': 'imageView'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
@ -367,3 +368,8 @@ class ArticleViewItem extends App.ObserverController
|
||||||
|
|
||||||
remove: =>
|
remove: =>
|
||||||
@el.remove()
|
@el.remove()
|
||||||
|
|
||||||
|
imageView: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
new App.TicketZoomArticleImageView(image: $(e.target).get(0).outerHTML)
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
<%- @Icon('diagonal-cross') %>
|
<%- @Icon('diagonal-cross') %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% if @head: %>
|
||||||
<h1 class="modal-title"><% if @headPrefix: %><%- @T(@headPrefix) %>: <% end %><%- @T(@head) %></h1>
|
<h1 class="modal-title"><% if @headPrefix: %><%- @T(@headPrefix) %>: <% end %><%- @T(@head) %></h1>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<%- @content %>
|
<%- @content %>
|
||||||
|
|
|
@ -48,22 +48,7 @@ module CreatesTicketArticles
|
||||||
# store dataurl images to store
|
# store dataurl images to store
|
||||||
attachments_inline = []
|
attachments_inline = []
|
||||||
if article.body && article.content_type =~ %r{text/html}i
|
if article.body && article.content_type =~ %r{text/html}i
|
||||||
article.body.gsub!( %r{(<img\s.?src=")(data:image/(jpeg|png);base64,.+?)"(|.+?)>}im ) { |_item|
|
(article.body, attachments_inline) = HtmlSanitizer.replace_inline_images(article.body, ticket.id)
|
||||||
file_attributes = StaticAssets.data_url_attributes($2)
|
|
||||||
cid = "#{ticket.id}.#{rand(999_999_999)}@#{Setting.get('fqdn')}"
|
|
||||||
attachment = {
|
|
||||||
data: file_attributes[:content],
|
|
||||||
filename: cid,
|
|
||||||
preferences: {
|
|
||||||
'Content-Type' => file_attributes[:mime_type],
|
|
||||||
'Mime-Type' => file_attributes[:mime_type],
|
|
||||||
'Content-ID' => cid,
|
|
||||||
'Content-Disposition' => 'inline',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
attachments_inline.push attachment
|
|
||||||
"#{$1}cid:#{cid}\">"
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# find attachments in upload cache
|
# find attachments in upload cache
|
||||||
|
|
|
@ -270,6 +270,9 @@ returns
|
||||||
}
|
}
|
||||||
attributes['attachments'].push item
|
attributes['attachments'].push item
|
||||||
}
|
}
|
||||||
|
if articles['body'] && articles['content_type'] =~ %r{text/html}i
|
||||||
|
articles['body'] = HtmlSanitizer.dynamic_image_size(articles['body'])
|
||||||
|
end
|
||||||
Ticket::Article.insert_urls(attributes)
|
Ticket::Article.insert_urls(attributes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -352,6 +352,73 @@ cleanup html string:
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
reolace inline images with cid images
|
||||||
|
|
||||||
|
string = HtmlSanitizer.replace_inline_images(article.body)
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.replace_inline_images(string, prefix = rand(999_999_999))
|
||||||
|
attachments_inline = []
|
||||||
|
scrubber = Loofah::Scrubber.new do |node|
|
||||||
|
if node.name == 'img'
|
||||||
|
if node['src'] && node['src'] =~ %r{^(data:image/(jpeg|png);base64,.+?)$}i
|
||||||
|
file_attributes = StaticAssets.data_url_attributes($1)
|
||||||
|
cid = "#{prefix}.#{rand(999_999_999)}@#{Setting.get('fqdn')}"
|
||||||
|
attachment = {
|
||||||
|
data: file_attributes[:content],
|
||||||
|
filename: cid,
|
||||||
|
preferences: {
|
||||||
|
'Content-Type' => file_attributes[:mime_type],
|
||||||
|
'Mime-Type' => file_attributes[:mime_type],
|
||||||
|
'Content-ID' => cid,
|
||||||
|
'Content-Disposition' => 'inline',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
attachments_inline.push attachment
|
||||||
|
node['src'] = "cid:#{cid}"
|
||||||
|
end
|
||||||
|
Loofah::Scrubber::STOP
|
||||||
|
end
|
||||||
|
end
|
||||||
|
[Loofah.fragment(string).scrub!(scrubber).to_s, attachments_inline]
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
satinize style of img tags
|
||||||
|
|
||||||
|
string = HtmlSanitizer.dynamic_image_size(article.body)
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.dynamic_image_size(string)
|
||||||
|
scrubber = Loofah::Scrubber.new do |node|
|
||||||
|
if node.name == 'img'
|
||||||
|
if node['src']
|
||||||
|
style = 'max-width:100%;'
|
||||||
|
if node['style']
|
||||||
|
pears = node['style'].downcase.gsub(/\t|\n|\r/, '').split(';')
|
||||||
|
pears.each { |local_pear|
|
||||||
|
prop = local_pear.split(':')
|
||||||
|
next if !prop[0]
|
||||||
|
key = prop[0].strip
|
||||||
|
if key == 'height'
|
||||||
|
key = 'max-height'
|
||||||
|
end
|
||||||
|
style += "#{key}:#{prop[1]};"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
node['style'] = style
|
||||||
|
end
|
||||||
|
Loofah::Scrubber::STOP
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Loofah.fragment(string).scrub!(scrubber).to_s
|
||||||
|
end
|
||||||
|
|
||||||
private_class_method :cleanup_target
|
private_class_method :cleanup_target
|
||||||
private_class_method :add_link
|
private_class_method :add_link
|
||||||
private_class_method :url_same?
|
private_class_method :url_same?
|
||||||
|
|
|
@ -156,6 +156,11 @@ returns
|
||||||
result[:subject] = data[:main_object].subject_build(result[:subject])
|
result[:subject] = data[:main_object].subject_build(result[:subject])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# prepare scaling of images
|
||||||
|
if result[:body]
|
||||||
|
result[:body] = HtmlSanitizer.dynamic_image_size(result[:body])
|
||||||
|
end
|
||||||
|
|
||||||
NotificationFactory::Mailer.send(
|
NotificationFactory::Mailer.send(
|
||||||
recipient: data[:user],
|
recipient: data[:user],
|
||||||
subject: result[:subject],
|
subject: result[:subject],
|
||||||
|
|
|
@ -99,8 +99,8 @@ AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
assert_equal(Hash, result.class)
|
assert_equal(Hash, result.class)
|
||||||
assert_equal(nil, result['subject'])
|
assert_equal(nil, result['subject'])
|
||||||
assert_no_match(/some body <img src="cid:/, result['body'])
|
assert_no_match(/some body <img src="cid:.+?/, result['body'])
|
||||||
assert_match(%r{some body <img src="/api/v1/ticket_attachment/.}, result['body'])
|
assert_match(%r{some body <img src="/api/v1/ticket_attachment/.+?" alt="Red dot"}, result['body'])
|
||||||
assert_equal('text/html', result['content_type'])
|
assert_equal('text/html', result['content_type'])
|
||||||
assert_equal(@agent.id, result['updated_by_id'])
|
assert_equal(@agent.id, result['updated_by_id'])
|
||||||
assert_equal(@agent.id, result['created_by_id'])
|
assert_equal(@agent.id, result['created_by_id'])
|
||||||
|
|
|
@ -848,6 +848,18 @@ christian.schaefer@example.com'
|
||||||
result = 'john.smith2@example.com'
|
result = 'john.smith2@example.com'
|
||||||
assert_equal(result, html.html2html_strict)
|
assert_equal(result, html.html2html_strict)
|
||||||
|
|
||||||
|
html = '<img src="/some.png" style="color: blue; width: 30px; height: 50px">'
|
||||||
|
result = '<img src="/some.png" style=" width: 30px; height: 50px;">'
|
||||||
|
assert_equal(result, html.html2html_strict)
|
||||||
|
|
||||||
|
html = '<img src="/some.png" width="30px" height="50px">'
|
||||||
|
result = '<img src="/some.png" style="width:30px;height:50px;">'
|
||||||
|
assert_equal(result, html.html2html_strict)
|
||||||
|
|
||||||
|
html = '<img style="width: 181px; height: 125px" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...">'
|
||||||
|
result = '<img style="width: 181px; height: 125px;" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...">'
|
||||||
|
assert_equal(result, html.html2html_strict)
|
||||||
|
|
||||||
html = '<p class="MsoNormal"><a href="http://www.example.de/"><span style="color:blue;text-decoration:none"><img border="0" width="30" height="30" id="_x0000_i1030" src="cid:image001.png@01D172FC.F323CDB0"></span></a><o:p></o:p></p>'
|
html = '<p class="MsoNormal"><a href="http://www.example.de/"><span style="color:blue;text-decoration:none"><img border="0" width="30" height="30" id="_x0000_i1030" src="cid:image001.png@01D172FC.F323CDB0"></span></a><o:p></o:p></p>'
|
||||||
#result = '<p>http://www.example.de/ <a href="http://www.example.de/" rel="nofollow" target="_blank"><img border="0" src="cid:image001.png@01D172FC.F323CDB0" style="width:30px;height:30px;"></a></p>'
|
#result = '<p>http://www.example.de/ <a href="http://www.example.de/" rel="nofollow" target="_blank"><img border="0" src="cid:image001.png@01D172FC.F323CDB0" style="width:30px;height:30px;"></a></p>'
|
||||||
result = '<p><a href="http://www.example.de/" rel="nofollow" target="_blank">http://www.example.de/</a></p>'
|
result = '<p><a href="http://www.example.de/" rel="nofollow" target="_blank">http://www.example.de/</a></p>'
|
||||||
|
@ -864,6 +876,51 @@ christian.schaefer@example.com'
|
||||||
assert_equal(result, html.html2html_strict)
|
assert_equal(result, html.html2html_strict)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'inline attachment replace' do
|
||||||
|
html = '<img style="width: 181px; height: 125px" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...">'
|
||||||
|
(body, attachments_inline) = HtmlSanitizer.replace_inline_images(html)
|
||||||
|
assert_match(/<img style="width: 181px; height: 125px" src="cid:.+?">/, body)
|
||||||
|
assert(1, attachments_inline.count)
|
||||||
|
|
||||||
|
html = '<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/..." style="width: 181px; height: 125px" alt="abc">'
|
||||||
|
(body, attachments_inline) = HtmlSanitizer.replace_inline_images(html)
|
||||||
|
assert_match(/<img src="cid:.+?" style="width: 181px; height: 125px" alt="abc">/, body)
|
||||||
|
assert(1, attachments_inline.count)
|
||||||
|
|
||||||
|
html = '<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/..." style="width: 181px; height: 125px" alt="abc"><invalid what ever'
|
||||||
|
(body, attachments_inline) = HtmlSanitizer.replace_inline_images(html)
|
||||||
|
assert_match(/<img src="cid:.+?" style="width: 181px; height: 125px" alt="abc">/, body)
|
||||||
|
assert(1, attachments_inline.count)
|
||||||
|
|
||||||
|
html = '<img src="/some_one.png" style="width: 181px; height: 125px" alt="abc">'
|
||||||
|
(body, attachments_inline) = HtmlSanitizer.replace_inline_images(html)
|
||||||
|
assert_match(/<img src="\/some_one.png" style="width: 181px; height: 125px" alt="abc">/, body)
|
||||||
|
assert(0, attachments_inline.count)
|
||||||
|
|
||||||
|
html = '<div><img style="width: 181px; height: 125px" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/..."><p>123</p><img style="width: 181px; height: 125px" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/..."></div>'
|
||||||
|
(body, attachments_inline) = HtmlSanitizer.replace_inline_images(html)
|
||||||
|
assert_match(/<div>\s+<img style="width: 181px; height: 125px" src="cid:.+?"><p>123<\/p>\s+<img style="width: 181px; height: 125px" src="cid:.+?">\s+<\/div>/, body)
|
||||||
|
assert(2, attachments_inline.count)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'set dynamic image size' do
|
||||||
|
html = '<img style="width: 181px; height: 125px" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...">'
|
||||||
|
body = HtmlSanitizer.dynamic_image_size(html)
|
||||||
|
assert_match(/<img style="max-width:100%;width: 181px;max-height: 125px;" src="data:image.+?">/, body)
|
||||||
|
|
||||||
|
html = '<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/..." style="width: 181px; height: 125px" alt="abc">'
|
||||||
|
body = HtmlSanitizer.dynamic_image_size(html)
|
||||||
|
assert_match(/<img src="data:image.+?" style="max-width:100%;width: 181px;max-height: 125px;" alt="abc">/, body)
|
||||||
|
|
||||||
|
html = '<img src="/some_one.png" style="width: 181px; height: 125px" alt="abc">'
|
||||||
|
body = HtmlSanitizer.dynamic_image_size(html)
|
||||||
|
assert_match(/<img src="\/some_one.png" style="max-width:100%;width: 181px;max-height: 125px;" alt="abc">/, body)
|
||||||
|
|
||||||
|
html = '<img src="/some_one.png" alt="abc">'
|
||||||
|
body = HtmlSanitizer.dynamic_image_size(html)
|
||||||
|
assert_match(/<img src="\/some_one.png" alt="abc" style="max-width:100%;">/, body)
|
||||||
|
end
|
||||||
|
|
||||||
test 'signature_identify function' do
|
test 'signature_identify function' do
|
||||||
marker_template = '######SIGNATURE_MARKER######'
|
marker_template = '######SIGNATURE_MARKER######'
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue