FIX: Make discobot certificate faster/non blocking (#11344)
This moves the way we add the user avatar and site logo to the discobot certificates from embeded base64 png to just using the files urls in the href to the image tag. This will make generation faster and the certificate smaller overall, but it can't be used in a `img` tag anymore, since SVGs in `img` tags don't load the external images In order to work around that we will move the certificate in posts to an iframe, which works fine without any user visible changes. For this to be possible the plugin automatically adds the site current domain to the list of allowed iframe origins.
This commit is contained in:
parent
ca4a962766
commit
bf5611f7eb
|
@ -137,7 +137,7 @@ module DiscourseNarrativeBot
|
||||||
src = Discourse.base_url + DiscourseNarrativeBot::Engine.routes.url_helpers.certificate_path(options)
|
src = Discourse.base_url + DiscourseNarrativeBot::Engine.routes.url_helpers.certificate_path(options)
|
||||||
alt = CGI.escapeHTML(I18n.t("#{self.class::I18N_KEY}.certificate.alt"))
|
alt = CGI.escapeHTML(I18n.t("#{self.class::I18N_KEY}.certificate.alt"))
|
||||||
|
|
||||||
"<img class='discobot-certificate' src='#{src}' width='650' height='464' alt='#{alt}'>"
|
"<iframe class='discobot-certificate' src='#{src}' width='650' height='464' alt='#{alt}'>"
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
module DiscourseNarrativeBot
|
module DiscourseNarrativeBot
|
||||||
class CertificateGenerator
|
class CertificateGenerator
|
||||||
def initialize(user, date, avatar_data)
|
def initialize(user, date, avatar_url)
|
||||||
@user = user
|
@user = user
|
||||||
@avatar_data = avatar_data
|
@avatar_url = avatar_url
|
||||||
|
|
||||||
date =
|
date =
|
||||||
begin
|
begin
|
||||||
|
@ -23,14 +23,14 @@ module DiscourseNarrativeBot
|
||||||
|
|
||||||
def new_user_track
|
def new_user_track
|
||||||
svg_default_width = 538.583
|
svg_default_width = 538.583
|
||||||
logo_container = logo_group(55, svg_default_width, 350)
|
logo_container = logo_group(55, svg_default_width, 280)
|
||||||
|
|
||||||
ApplicationController.render(inline: read_template('new_user'), assigns: assign_options(svg_default_width, logo_container))
|
ApplicationController.render(inline: read_template('new_user'), assigns: assign_options(svg_default_width, logo_container))
|
||||||
end
|
end
|
||||||
|
|
||||||
def advanced_user_track
|
def advanced_user_track
|
||||||
svg_default_width = 722.8
|
svg_default_width = 722.8
|
||||||
logo_container = logo_group(40, svg_default_width, 280)
|
logo_container = logo_group(40, svg_default_width, 350)
|
||||||
|
|
||||||
ApplicationController.render(inline: read_template('advanced_user'), assigns: assign_options(svg_default_width, logo_container))
|
ApplicationController.render(inline: read_template('advanced_user'), assigns: assign_options(svg_default_width, logo_container))
|
||||||
end
|
end
|
||||||
|
@ -46,7 +46,7 @@ module DiscourseNarrativeBot
|
||||||
width: width,
|
width: width,
|
||||||
discobot_user: @discobot_user,
|
discobot_user: @discobot_user,
|
||||||
date: @date,
|
date: @date,
|
||||||
avatar_url: base64_image_data(@avatar_data),
|
avatar_url: @avatar_url,
|
||||||
logo_group: logo_group,
|
logo_group: logo_group,
|
||||||
name: name
|
name: name
|
||||||
}
|
}
|
||||||
|
@ -59,48 +59,16 @@ module DiscourseNarrativeBot
|
||||||
def logo_group(size, width, height)
|
def logo_group(size, width, height)
|
||||||
return unless SiteSetting.site_logo_small_url.present?
|
return unless SiteSetting.site_logo_small_url.present?
|
||||||
|
|
||||||
begin
|
uri = URI(SiteSetting.site_logo_small_url)
|
||||||
uri = URI(SiteSetting.site_logo_small_url)
|
|
||||||
|
|
||||||
logo_uri =
|
logo_uri =
|
||||||
if uri.host.blank? || uri.scheme.blank?
|
if uri.host.blank? || uri.scheme.blank?
|
||||||
URI("#{Discourse.base_url}/#{uri.path}")
|
URI("#{Discourse.base_url}/#{uri.path}")
|
||||||
else
|
else
|
||||||
uri
|
uri
|
||||||
end
|
end
|
||||||
|
|
||||||
<<~URL
|
{ size: size, width: width, height: height, logo_uri: logo_uri }
|
||||||
<g transform="translate(#{width / 2 - (size / 2)} #{height})">
|
|
||||||
<image height="#{size}px" width="#{size}px" #{base64_image_link(logo_uri)}/>
|
|
||||||
</g>
|
|
||||||
URL
|
|
||||||
rescue URI::InvalidURIError
|
|
||||||
''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def base64_image_data(data)
|
|
||||||
return "" if data.blank?
|
|
||||||
"xlink:href=\"data:image/png;base64,#{Base64.strict_encode64(data)}\""
|
|
||||||
end
|
|
||||||
|
|
||||||
def base64_image_link(url)
|
|
||||||
if image = fetch_image(url)
|
|
||||||
base64_image_data(image)
|
|
||||||
else
|
|
||||||
""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_image(url)
|
|
||||||
FileHelper.download(
|
|
||||||
url.to_s,
|
|
||||||
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
|
|
||||||
tmp_file_name: 'narrative-bot-logo',
|
|
||||||
follow_redirect: true
|
|
||||||
)&.read
|
|
||||||
rescue OpenURI::HTTPError
|
|
||||||
# Ignore if fetching image returns a non 200 response
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 729 KiB After Width: | Height: | Size: 729 KiB |
|
@ -452,12 +452,14 @@
|
||||||
<clipPath id="clipCircle">
|
<clipPath id="clipCircle">
|
||||||
<circle r="15" cx="15" cy="15"/>
|
<circle r="15" cx="15" cy="15"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<image clip-path="url(#clipCircle)" height="30px" width="30px" <%= sanitize(@avatar_url) %>/>
|
<image clip-path="url(#clipCircle)" height="30px" width="30px" href="<%= @avatar_url %>"/>
|
||||||
</g>
|
</g>
|
||||||
<text x="<%= @width / 2 %>" y="240.94" text-anchor="middle" font-size="24" fill="#020403" font-family="Tangerine, Tangerine">
|
<text x="<%= @width / 2 %>" y="240.94" text-anchor="middle" font-size="24" fill="#020403" font-family="Tangerine, Tangerine">
|
||||||
<%= @name %>
|
<%= @name %>
|
||||||
</text>
|
</text>
|
||||||
<%== @logo_group %>
|
<g transform="translate(<%= @logo_group[:width] / 2 - (@logo_group[:size] / 2) %> <%= @logo_group[:height] %>)">
|
||||||
|
<image height="<%= @logo_group[:size] %>px" width="<%= @logo_group[:size] %>px" href="<%= @logo_group[:logo_uri] %>"/>
|
||||||
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g id="Layer_1-2" data-name="Layer 1">
|
<g id="Layer_1-2" data-name="Layer 1">
|
||||||
<path d="M339.328 47.187c-.034 1.193 3.25 2.2 6.51 3.383 5.5 1.994 13.1 3.73 17.68 3.112 5.05-.55 4.982-2.217 4.054-4.8-.1-4.647.258-9.493.156-14.142l-27.128-.458c.5 4.698-1.778 8.207-1.272 12.905z" transform="translate(-.108)" fill-rule="evenodd" fill="url(#GradientFill_24)"/>
|
<path d="M339.328 47.187c-.034 1.193 3.25 2.2 6.51 3.383 5.5 1.994 13.1 3.73 17.68 3.112 5.05-.55 4.982-2.217 4.054-4.8-.1-4.647.258-9.493.156-14.142l-27.128-.458c.5 4.698-1.778 8.207-1.272 12.905z" transform="translate(-.108)" fill-rule="evenodd" fill="url(#GradientFill_24)"/>
|
||||||
|
|
Before Width: | Height: | Size: 494 KiB After Width: | Height: | Size: 495 KiB |
|
@ -50,6 +50,11 @@ after_initialize do
|
||||||
# Disable welcome message because that is what the bot is supposed to replace.
|
# Disable welcome message because that is what the bot is supposed to replace.
|
||||||
SiteSetting.send_welcome_message = false if SiteSetting.send_welcome_message
|
SiteSetting.send_welcome_message = false if SiteSetting.send_welcome_message
|
||||||
|
|
||||||
|
certificate_path = "#{Discourse.base_url}/discobot/certificate.svg"
|
||||||
|
if SiteSetting.discourse_narrative_bot_enabled && !SiteSetting.allowed_iframes.include?(certificate_path)
|
||||||
|
SiteSetting.allowed_iframes = SiteSetting.allowed_iframes.split('|').append("#{Discourse.base_url}/discobot/certificate.svg").join('|')
|
||||||
|
end
|
||||||
|
|
||||||
require_dependency 'plugin_store'
|
require_dependency 'plugin_store'
|
||||||
|
|
||||||
module ::DiscourseNarrativeBot
|
module ::DiscourseNarrativeBot
|
||||||
|
@ -94,8 +99,7 @@ after_initialize do
|
||||||
raise Discourse::NotFound if user.blank?
|
raise Discourse::NotFound if user.blank?
|
||||||
|
|
||||||
hijack do
|
hijack do
|
||||||
avatar_data = fetch_avatar(user)
|
generator = CertificateGenerator.new(user, params[:date], avatar_url(user))
|
||||||
generator = CertificateGenerator.new(user, params[:date], avatar_data)
|
|
||||||
|
|
||||||
svg = params[:type] == 'advanced' ? generator.advanced_user_track : generator.new_user_track
|
svg = params[:type] == 'advanced' ? generator.advanced_user_track : generator.new_user_track
|
||||||
|
|
||||||
|
@ -107,16 +111,8 @@ after_initialize do
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fetch_avatar(user)
|
def avatar_url(user)
|
||||||
avatar_url = UrlHelper.absolute(Discourse.base_path + user.avatar_template.gsub('{size}', '250'))
|
UrlHelper.absolute(Discourse.base_path + user.avatar_template.gsub('{size}', '250'))
|
||||||
FileHelper.download(
|
|
||||||
avatar_url.to_s,
|
|
||||||
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
|
|
||||||
tmp_file_name: 'narrative-bot-avatar',
|
|
||||||
follow_redirect: true
|
|
||||||
)&.read
|
|
||||||
rescue OpenURI::HTTPError
|
|
||||||
# Ignore if fetching image returns a non 200 response
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,7 +36,9 @@ describe "Discobot Certificate" do
|
||||||
get '/discobot/certificate.svg', params: params
|
get '/discobot/certificate.svg', params: params
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(response.body).to include('<image height="55px" width="55px" />')
|
expect(response.body).to include('<svg')
|
||||||
|
expect(response.body).to include(user.avatar_template.gsub('{size}', '250'))
|
||||||
|
expect(response.body).to include(SiteSetting.site_logo_small_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when params are missing' do
|
describe 'when params are missing' do
|
||||||
|
|
Loading…
Reference in New Issue