Send inline images with cid (load images via http/https in src).

This commit is contained in:
Martin Edenhofer 2016-05-10 15:06:51 +02:00
parent 530c844f68
commit 549978d731
9 changed files with 94 additions and 18 deletions

View file

@ -129,7 +129,7 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password}
# validate image # validate image
if params[:logo] && params[:logo] =~ /^data:image/i if params[:logo] && params[:logo] =~ /^data:image/i
file = StaticAssets.data_url_attributes( params[:logo] ) file = StaticAssets.data_url_attributes(params[:logo])
if !file[:content] || !file[:mime_type] if !file[:content] || !file[:mime_type]
messages[:logo] = 'Unable to process image upload.' messages[:logo] = 'Unable to process image upload.'
@ -163,19 +163,19 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password}
if params[:logo] && params[:logo] =~ /^data:image/i if params[:logo] && params[:logo] =~ /^data:image/i
# data:image/png;base64 # data:image/png;base64
file = StaticAssets.data_url_attributes( params[:logo] ) file = StaticAssets.data_url_attributes(params[:logo])
# store image 1:1 # store image 1:1
StaticAssets.store_raw( file[:content], file[:mime_type] ) StaticAssets.store_raw(file[:content], file[:mime_type])
end end
if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i
# data:image/png;base64 # data:image/png;base64
file = StaticAssets.data_url_attributes( params[:logo_resize] ) file = StaticAssets.data_url_attributes(params[:logo_resize])
# store image 1:1 # store image 1:1
settings[:product_logo] = StaticAssets.store( file[:content], file[:mime_type] ) settings[:product_logo] = StaticAssets.store(file[:content], file[:mime_type])
end end
# set changed settings # set changed settings

View file

@ -67,7 +67,7 @@ class SettingsController < ApplicationController
if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i
# data:image/png;base64 # data:image/png;base64
file = StaticAssets.data_url_attributes( params[:logo_resize] ) file = StaticAssets.data_url_attributes(params[:logo_resize])
# store image 1:1 # store image 1:1
setting.state = StaticAssets.store( file[:content], file[:mime_type] ) setting.state = StaticAssets.store( file[:content], file[:mime_type] )

View file

@ -250,7 +250,7 @@ class TicketsController < ApplicationController
links.each { |item| links.each { |item|
link_list.push item link_list.push item
if item['link_object'] == 'Ticket' if item['link_object'] == 'Ticket'
linked_ticket = Ticket.lookup( id: item['link_object_value'] ) linked_ticket = Ticket.lookup(id: item['link_object_value'])
assets = linked_ticket.assets(assets) assets = linked_ticket.assets(assets)
end end
} }
@ -534,9 +534,31 @@ class TicketsController < ApplicationController
# create article if given # create article if given
form_id = params[:form_id] form_id = params[:form_id]
params.delete(:form_id) params.delete(:form_id)
article = Ticket::Article.new( Ticket::Article.param_validation( params ) ) article = Ticket::Article.new(Ticket::Article.param_validation( params ))
article.ticket_id = ticket.id article.ticket_id = ticket.id
# store dataurl images to store
if form_id && article.body && article.content_type =~ %r{text/html}i
article.body.gsub!( %r{(<img\s.+?src=")(data:image/(jpeg|png);base64,.+?)">}i ) { |_item|
file_attributes = StaticAssets.data_url_attributes($2)
cid = "#{ticket.id}.#{form_id}.#{rand(999_999)}@#{Setting.get('fqdn')}"
headers_store = {
'Content-Type' => file_attributes[:mime_type],
'Mime-Type' => file_attributes[:mime_type],
'Content-ID' => cid,
'Content-Disposition' => 'inline',
}
store = Store.add(
object: 'UploadCache',
o_id: form_id,
data: file_attributes[:content],
filename: cid,
preferences: headers_store
)
"#{$1}cid:#{cid}\">"
}
end
# find attachments in upload cache # find attachments in upload cache
if form_id if form_id
article.attachments = Store.list( article.attachments = Store.list(

View file

@ -82,9 +82,23 @@ module Channel::EmailBuild
if html_alternative if html_alternative
html_container = Mail::Part.new { content_type 'multipart/related' } html_container = Mail::Part.new { content_type 'multipart/related' }
html_container.add_part html_alternative html_container.add_part html_alternative
alternative_bodies.add_part html_container
# place to add inline attachments related to html alternative # place to add inline attachments related to html alternative
if attr[:attachments]
attr[:attachments].each do |attachment|
next if attachment.class == Hash
next if attachment.preferences['Content-ID'].empty?
attachment = Mail::Part.new do
content_type attachment.preferences['Content-Type']
content_id attachment.preferences['Content-ID']
content_disposition attachment.preferences['Content-Disposition'] || 'inline'
content_transfer_encoding 'binary'
body attachment.content.force_encoding('BINARY')
end
html_container.add_part attachment
end
end
alternative_bodies.add_part html_container
end end
mail.add_part alternative_bodies mail.add_part alternative_bodies
@ -96,11 +110,12 @@ module Channel::EmailBuild
attachment['content-id'] = nil attachment['content-id'] = nil
mail.attachments[ attachment[:filename] ] = attachment mail.attachments[ attachment[:filename] ] = attachment
else else
next if !attachment.preferences['Content-ID'].empty?
mail.attachments[attachment.filename] = { mail.attachments[attachment.filename] = {
:content_type => attachment.preferences['Content-Type'], content_disposition: attachment.preferences['Content-Disposition'] || 'attachment',
:mime_type => attachment.preferences['Mime-Type'], content_type: attachment.preferences['Content-Type'],
:content => attachment.content, mime_type: attachment.preferences['Mime-Type'],
'content-id' => nil, content: attachment.content,
} }
end end
end end

View file

@ -18,8 +18,8 @@ add an attachment to storage
o_id: 4711, o_id: 4711,
data: binary_string, data: binary_string,
preferences: { preferences: {
:content_type => 'image/png', content_type: 'image/png',
:content_id => 234, content_id: 234,
} }
) )
@ -69,8 +69,8 @@ returns
size: 94123, size: 94123,
filename: 'image.png', filename: 'image.png',
preferences: { preferences: {
:content_type => 'image/png', content_type: 'image/png',
:content_id => 234, content_id: 234,
} }
} }
store1.content # binary_string store1.content # binary_string

View file

@ -37,6 +37,30 @@ class Ticket::Article < ApplicationModel
self.message_id_md5 = Digest::MD5.hexdigest(message_id.to_s) self.message_id_md5 = Digest::MD5.hexdigest(message_id.to_s)
end end
# insert inline image urls
def self.insert_urls(article, attachments)
inline_attachments = {}
article['body'].gsub!( /(<img\s(style.+?|)src=")cid:(.+?)(">)/i ) { |item|
replace = item
# look for attachment
attachments.each {|file|
next if !file.preferences['Content-ID'] || file.preferences['Content-ID'] != $3
replace = "#{$1}/api/v1/ticket_attachment/#{article['ticket_id']}/#{article['id']}/#{file.id}#{$4}"
inline_attachments[file.id] = true
break
}
replace
}
new_attachments = []
attachments.each {|file|
next if inline_attachments[file.id]
new_attachments.push file
}
article['attachments'] = new_attachments
article
end
private private
# strip not wanted chars # strip not wanted chars

View file

@ -39,6 +39,20 @@ returns
# add attachment list to article # add attachment list to article
data[ Ticket::Article.to_app_model ][ id ]['attachments'] = attachments data[ Ticket::Article.to_app_model ][ id ]['attachments'] = attachments
if !data[ Ticket::Article.to_app_model ][ id ]['attachments'].empty?
if data[ Ticket::Article.to_app_model ][ id ]['content_type'] =~ %r{text/html}i
if data[ Ticket::Article.to_app_model ][ id ]['body'] =~ /<img/i
# insert inline images with urls
attributes = Ticket::Article.insert_urls(
data[ Ticket::Article.to_app_model ][ id ],
data[ Ticket::Article.to_app_model ][ id ]['attachments']
)
data[ Ticket::Article.to_app_model ][ id ] = attributes
end
end
end
end end
%w(created_by_id updated_by_id).each {|local_user_id| %w(created_by_id updated_by_id).each {|local_user_id|

View file

@ -7,5 +7,6 @@ class ContenteditableIamges < ActiveRecord::Migration
change_column :text_modules, :content, :text, limit: 10.megabytes + 1, null: false change_column :text_modules, :content, :text, limit: 10.megabytes + 1, null: false
change_column :signatures, :body, :text, limit: 10.megabytes + 1, null: true change_column :signatures, :body, :text, limit: 10.megabytes + 1, null: true
change_column :ticket_articles, :body, :text, limit: 20.megabytes + 1, null: false change_column :ticket_articles, :body, :text, limit: 20.megabytes + 1, null: false
change_column :taskbars, :state, :text, limit: 20.megabytes + 1, null: true
end end
end end

View file

@ -27,7 +27,7 @@ returns
store image 1:1 in backend and return filename store image 1:1 in backend and return filename
filename = StaticAssets.store_raw( content, content_type ) filename = StaticAssets.store_raw(content, content_type)
returns returns