2013-02-05 14:16:51 -05:00
# Post processing that we can do after a post has already been cooked. For
# example, inserting the onebox content, or image sizes.
require_dependency 'oneboxer'
class CookedPostProcessor
2013-06-21 18:29:40 +02:00
include ActionView :: Helpers :: NumberHelper
2013-02-19 17:57:14 +11:00
2013-02-05 14:16:51 -05:00
def initialize ( post , opts = { } )
@dirty = false
@opts = opts
@post = post
2013-04-10 17:52:38 +10:00
@doc = Nokogiri :: HTML :: fragment ( post . cooked )
2013-02-19 17:57:14 +11:00
@size_cache = { }
2013-06-15 12:29:20 +02:00
@has_been_uploaded_cache = { }
2013-02-05 14:16:51 -05:00
end
2013-06-15 12:29:20 +02:00
def post_process
return unless @doc . present?
post_process_images
post_process_oneboxes
2013-02-05 14:16:51 -05:00
end
2013-02-25 19:42:20 +03:00
def post_process_images
2013-06-24 02:10:21 +02:00
images = @doc . css ( " img " ) - @doc . css ( " .onebox-result img " )
2013-02-25 19:42:20 +03:00
return unless images . present?
2013-02-05 14:16:51 -05:00
2013-02-19 17:57:14 +11:00
images . each do | img |
2013-06-16 10:21:01 +02:00
# keep track of the original src
2013-02-19 17:57:14 +11:00
src = img [ 'src' ]
2013-06-15 12:29:20 +02:00
# make sure the src is absolute (when working with locally uploaded files)
img [ 'src' ] = Discourse . base_url_no_prefix + img [ 'src' ] if img [ 'src' ] =~ / ^ \/ [^ \/ ] /
2013-02-05 14:16:51 -05:00
2013-04-13 16:31:20 +02:00
if src . present?
2013-06-16 10:21:01 +02:00
# make sure the img has both width and height attributes
2013-06-15 12:29:20 +02:00
update_dimensions! ( img )
2013-06-17 01:00:25 +02:00
# retrieve the associated upload, if any
upload = get_upload_from_url ( img [ 'src' ] )
if upload . present?
2013-06-17 22:46:48 +02:00
# update reverse index
associate_to_post upload
2013-06-17 01:00:25 +02:00
# create a thumbnail
upload . create_thumbnail!
# optimize image
img [ 'src' ] = optimize_image ( img )
# lightbox treatment
2013-06-21 18:29:40 +02:00
convert_to_link! ( img , upload )
2013-06-17 01:00:25 +02:00
else
convert_to_link! ( img )
end
2013-06-15 12:29:20 +02:00
# mark the post as dirty whenever the src has changed
@dirty |= src != img [ 'src' ]
2013-02-05 14:16:51 -05:00
end
2013-04-13 16:31:20 +02:00
end
2013-02-19 17:57:14 +11:00
2013-04-13 16:31:20 +02:00
# Extract the first image from the first post and use it as the 'topic image'
if @post . post_number == 1
img = images . first
@post . topic . update_column :image_url , img [ 'src' ] if img [ 'src' ] . present?
2013-02-19 17:57:14 +11:00
end
2013-04-13 16:31:20 +02:00
2013-02-19 17:57:14 +11:00
end
2013-06-15 12:29:20 +02:00
def post_process_oneboxes
args = { post_id : @post . id }
args [ :invalidate_oneboxes ] = true if @opts [ :invalidate_oneboxes ]
# bake onebox content into the post
result = Oneboxer . apply ( @doc ) do | url , element |
Oneboxer . onebox ( url , args )
end
# mark the post as dirty whenever a onebox as been baked
@dirty |= result . changed?
end
2013-04-13 16:31:20 +02:00
2013-06-15 12:29:20 +02:00
def update_dimensions! ( img )
return if img [ 'width' ] . present? && img [ 'height' ] . present?
w , h = get_size_from_image_sizes ( img [ 'src' ] , @opts [ :image_sizes ] ) || image_dimensions ( img [ 'src' ] )
if w && h
img [ 'width' ] = w . to_s
img [ 'height' ] = h . to_s
@dirty = true
end
end
2013-02-21 12:07:36 +11:00
2013-06-17 01:00:25 +02:00
def get_upload_from_url ( url )
2013-06-22 13:38:42 +02:00
if Upload . has_been_uploaded? ( url )
if m = LocalStore . uploaded_regex . match ( url )
Upload . where ( id : m [ :upload_id ] ) . first
elsif Upload . is_on_s3? ( url )
Upload . where ( url : url ) . first
end
2013-06-17 01:00:25 +02:00
end
end
2013-06-17 22:46:48 +02:00
def associate_to_post ( upload )
return if PostUpload . where ( post_id : @post . id , upload_id : upload . id ) . count > 0
PostUpload . create ( { post_id : @post . id , upload_id : upload . id } )
rescue ActiveRecord :: RecordNotUnique
# do not care if it's already associated
end
2013-06-15 12:29:20 +02:00
def optimize_image ( img )
return img [ " src " ]
2013-06-16 10:21:01 +02:00
# 1) optimize using image_optim
# 2) .png vs. .jpg
2013-02-19 17:57:14 +11:00
end
2013-06-21 18:29:40 +02:00
def convert_to_link! ( img , upload = nil )
2013-02-19 17:57:14 +11:00
src = img [ " src " ]
2013-04-13 16:31:20 +02:00
width , height = img [ " width " ] . to_i , img [ " height " ] . to_i
2013-02-19 17:57:14 +11:00
return unless src . present? && width > SiteSetting . auto_link_images_wider_than
2013-04-13 16:31:20 +02:00
original_width , original_height = get_size ( src )
2013-02-19 17:57:14 +11:00
return unless original_width . to_i > width && original_height . to_i > height
parent = img . parent
while parent
return if parent . name == " a "
break unless parent . respond_to? :parent
parent = parent . parent
end
2013-02-25 19:42:20 +03:00
# not a hyperlink so we can apply
2013-06-21 14:32:08 -04:00
img [ 'src' ] = upload . thumbnail_url if ( upload && upload . thumbnail_url . present? )
2013-06-26 02:44:20 +02:00
# first, create a div to hold our lightbox
lightbox = Nokogiri :: XML :: Node . new " div " , @doc
img . add_next_sibling lightbox
lightbox . add_child img
# then, the link to our larger image
2013-02-19 17:57:14 +11:00
a = Nokogiri :: XML :: Node . new " a " , @doc
img . add_next_sibling ( a )
a [ " href " ] = src
a [ " class " ] = " lightbox "
a . add_child ( img )
2013-06-26 02:44:20 +02:00
# then, some overlay informations
meta = Nokogiri :: XML :: Node . new " div " , @doc
meta [ " class " ] = " meta "
img . add_next_sibling meta
2013-06-21 18:29:40 +02:00
2013-06-26 21:53:31 +02:00
filename = get_filename ( upload , src )
2013-06-21 18:29:40 +02:00
informations = " #{ original_width } x #{ original_height } "
informations << " | #{ number_to_human_size ( upload . filesize ) } " if upload
2013-06-26 02:44:20 +02:00
meta . add_child create_span_node ( " filename " , filename )
meta . add_child create_span_node ( " informations " , informations )
meta . add_child create_span_node ( " expand " )
2013-06-21 18:29:40 +02:00
2013-02-19 17:57:14 +11:00
@dirty = true
2013-06-21 18:29:40 +02:00
end
2013-02-19 17:57:14 +11:00
2013-06-26 21:53:31 +02:00
def get_filename ( upload , src )
return File . basename ( src ) unless upload
2013-07-04 00:39:23 +02:00
return upload . original_filename unless upload . original_filename =~ / ^blob( \ .png)?$ /i
2013-06-26 21:53:31 +02:00
return I18n . t ( 'upload.pasted_image_filename' )
end
2013-06-21 18:29:40 +02:00
def create_span_node ( klass , content = nil )
span = Nokogiri :: XML :: Node . new " span " , @doc
span . content = content if content
span [ 'class' ] = klass
span
2013-02-05 14:16:51 -05:00
end
2013-02-19 17:57:14 +11:00
def get_size_from_image_sizes ( src , image_sizes )
if image_sizes . present?
if dim = image_sizes [ src ]
ImageSizer . resize ( dim [ 'width' ] , dim [ 'height' ] )
end
end
end
2013-02-05 14:16:51 -05:00
2013-06-15 12:29:20 +02:00
# Retrieve the image dimensions for a url
def image_dimensions ( url )
w , h = get_size ( url )
ImageSizer . resize ( w , h ) if w && h
2013-02-05 14:16:51 -05:00
end
2013-06-15 12:29:20 +02:00
def get_size ( url )
2013-06-22 13:38:42 +02:00
# make sure s3 urls have a scheme (otherwise, FastImage will fail)
url = " http: " + url if Upload . is_on_s3? ( url )
return unless is_valid_image_uri? url
# we can *always* crawl our own images
2013-06-16 10:21:01 +02:00
return unless SiteSetting . crawl_images? || Upload . has_been_uploaded? ( url )
2013-06-15 12:29:20 +02:00
@size_cache [ url ] || = FastImage . size ( url )
rescue Zlib :: BufError # FastImage.size raises BufError for some gifs
2013-02-05 14:16:51 -05:00
end
2013-06-22 13:38:42 +02:00
def is_valid_image_uri? ( url )
2013-06-15 12:29:20 +02:00
uri = URI . parse ( url )
2013-06-22 13:38:42 +02:00
%w( http https ) . include? uri . scheme
2013-06-24 21:38:00 +02:00
rescue URI :: InvalidURIError
2013-04-10 17:52:38 +10:00
end
2013-06-15 12:29:20 +02:00
def dirty?
@dirty
end
def html
@doc . try ( :to_html )
2013-02-05 14:16:51 -05:00
end
end