discourse/app/jobs/regular/pull_hotlinked_images.rb

93 lines
3.4 KiB
Ruby
Raw Normal View History

require_dependency 'url_helper'
2014-04-15 04:55:57 +08:00
require_dependency 'file_helper'
2013-11-06 02:04:47 +08:00
module Jobs
class PullHotlinkedImages < Jobs::Base
include UrlHelper
2013-11-06 02:04:47 +08:00
def initialize
# maximum size of the file in bytes
@max_size = SiteSetting.max_image_size_kb.kilobytes
2013-11-06 02:04:47 +08:00
end
def execute(args)
return unless SiteSetting.download_remote_images_to_local?
2013-11-06 02:04:47 +08:00
post_id = args[:post_id]
raise Discourse::InvalidParameters.new(:post_id) unless post_id.present?
post = Post.where(id: post_id).first
return unless post.present?
raw = post.raw.dup
downloaded_urls = {}
extract_images_from(post.cooked).each do |image|
src = image['src']
src = "http:" + src if src.start_with?("//")
2013-11-06 02:04:47 +08:00
if is_valid_image_url(src)
begin
# have we already downloaded that file?
if !downloaded_urls.include?(src)
2014-04-15 04:55:57 +08:00
hotlinked = FileHelper.download(src, @max_size, "discourse-hotlinked") rescue Discourse::InvalidParameters
2013-11-16 06:28:16 +08:00
if hotlinked.try(:size) <= @max_size
2013-11-06 02:04:47 +08:00
filename = File.basename(URI.parse(src).path)
2014-04-15 04:55:57 +08:00
upload = Upload.create_for(post.user_id, hotlinked, filename, hotlinked.size, src)
2013-11-06 02:04:47 +08:00
downloaded_urls[src] = upload.url
else
2014-04-15 04:55:57 +08:00
Rails.logger.error("Failed to pull hotlinked image: #{src} - Image is bigger than #{@max_size}")
2013-11-06 02:04:47 +08:00
end
end
2013-12-21 15:19:22 +08:00
# have we successfully downloaded that file?
2013-11-06 02:04:47 +08:00
if downloaded_urls[src].present?
url = downloaded_urls[src]
escaped_src = src.gsub("?", "\\?").gsub(".", "\\.").gsub("+", "\\+")
# there are 6 ways to insert an image in a post
2013-11-06 02:04:47 +08:00
# HTML tag - <img src="http://...">
raw.gsub!(/src=["']#{escaped_src}["']/i, "src='#{url}'")
# BBCode tag - [img]http://...[/img]
raw.gsub!(/\[img\]#{escaped_src}\[\/img\]/i, "[img]#{url}[/img]")
# Markdown linked image - [![alt](http://...)](http://...)
raw.gsub!(/\[!\[([^\]]*)\]\(#{escaped_src}\)\]/) { "[<img src='#{url}' alt='#{$1}'>]" }
2013-11-06 02:04:47 +08:00
# Markdown inline - ![alt](http://...)
raw.gsub!(/!\[([^\]]*)\]\(#{escaped_src}\)/) { "![#{$1}](#{url})" }
# Markdown reference - [x]: http://
raw.gsub!(/\[(\d+)\]: #{escaped_src}/) { "[#{$1}]: #{url}" }
# Direct link
raw.gsub!(src, "<img src='#{url}'>")
end
rescue => e
2014-04-15 04:55:57 +08:00
Rails.logger.error("Failed to pull hotlinked image: #{src}\n" + e.message + "\n" + e.backtrace.join("\n"))
2013-11-06 02:04:47 +08:00
ensure
# close & delete the temp file
hotlinked && hotlinked.close!
end
end
end
# TODO: make sure the post hasn´t changed while we were downloading remote images
if raw != post.raw
2013-11-22 08:52:26 +08:00
options = { edit_reason: I18n.t("upload.edit_reason") }
options[:bypass_bump] = true if args[:bypass_bump] == true
2013-11-06 02:04:47 +08:00
post.revise(Discourse.system_user, raw, options)
end
end
def extract_images_from(html)
doc = Nokogiri::HTML::fragment(html)
doc.css("img") - doc.css(".onebox-result img") - doc.css("img.avatar")
end
def is_valid_image_url(src)
src.present? &&
!Discourse.store.has_been_uploaded?(src) &&
!src.start_with?(Discourse.asset_host || Discourse.base_url_no_prefix)
2013-11-06 02:04:47 +08:00
end
end
end