2013-02-05 14:16:51 -05:00
class UploadsController < ApplicationController
2013-09-06 19:18:42 +02:00
before_filter :ensure_logged_in , except : [ :show ]
2015-07-24 09:41:46 +10:00
skip_before_filter :preload_json , :check_xhr , :redirect_to_login_if_required , only : [ :show ]
2013-04-03 01:17:17 +02:00
2013-02-05 14:16:51 -05:00
def create
2015-05-20 01:39:58 +02:00
type = params . require ( :type )
2015-06-21 14:52:52 +03:00
file = params [ :file ] || params [ :files ] . try ( :first )
2015-05-20 01:39:58 +02:00
url = params [ :url ]
2015-05-28 15:08:54 +10:00
client_id = params [ :client_id ]
2015-06-15 16:12:15 +02:00
synchronous = is_api? && params [ :synchronous ]
2015-05-20 01:39:58 +02:00
2015-11-12 10:26:45 +01:00
if type == " avatar "
if SiteSetting . sso_overrides_avatar || ! SiteSetting . allow_uploaded_avatars
return render json : failed_json , status : 422
end
end
2015-06-15 16:12:15 +02:00
if synchronous
data = create_upload ( type , file , url )
render json : data . as_json
else
Scheduler :: Defer . later ( " Create Upload " ) do
data = create_upload ( type , file , url )
2015-05-28 15:08:54 +10:00
MessageBus . publish ( " /uploads/ #{ type } " , data . as_json , client_ids : [ client_id ] )
2015-05-20 17:38:06 +02:00
end
2015-06-15 16:12:15 +02:00
render json : success_json
2014-09-23 15:50:26 +10:00
end
2013-02-05 14:16:51 -05:00
end
2013-06-05 00:34:53 +02:00
2013-09-06 19:18:42 +02:00
def show
2014-05-14 10:51:09 +10:00
return render_404 if ! RailsMultisite :: ConnectionManagement . has_db? ( params [ :site ] )
2014-03-25 10:37:31 +11:00
RailsMultisite :: ConnectionManagement . with_connection ( params [ :site ] ) do | db |
2014-05-14 10:51:09 +10:00
return render_404 unless Discourse . store . internal?
2014-09-09 18:40:11 +02:00
return render_404 if SiteSetting . prevent_anons_from_downloading_files && current_user . nil?
2015-07-24 09:44:16 +10:00
return render_404 if SiteSetting . login_required? && db == " default " && current_user . nil?
2013-09-06 19:18:42 +02:00
2015-05-20 15:32:31 +02:00
if upload = Upload . find_by ( sha1 : params [ :sha ] ) || Upload . find_by ( id : params [ :id ] , url : request . env [ " PATH_INFO " ] )
2015-05-19 12:31:12 +02:00
opts = { filename : upload . original_filename }
2014-11-13 08:50:55 +11:00
opts [ :disposition ] = 'inline' if params [ :inline ]
2015-05-19 12:31:12 +02:00
send_file ( Discourse . store . path_for ( upload ) , opts )
2014-04-14 22:55:57 +02:00
else
2014-05-14 10:51:09 +10:00
render_404
2014-04-14 22:55:57 +02:00
end
2014-03-25 10:37:31 +11:00
end
2013-09-06 19:18:42 +02:00
end
2014-05-14 10:51:09 +10:00
protected
def render_404
render nothing : true , status : 404
end
2015-06-15 16:12:15 +02:00
def create_upload ( type , file , url )
begin
2016-04-06 22:51:28 +02:00
maximum_upload_size = [ SiteSetting . max_image_size_kb , SiteSetting . max_attachment_size_kb ] . max . kilobytes
2015-08-18 11:39:51 +02:00
# ensure we have a file
if file . nil?
# API can provide a URL
if url . present? && is_api?
2016-04-06 22:51:28 +02:00
tempfile = FileHelper . download ( url , maximum_upload_size , " discourse-upload- #{ type } " ) rescue nil
2015-08-18 11:39:51 +02:00
filename = File . basename ( URI . parse ( url ) . path )
end
2015-06-15 16:12:15 +02:00
else
tempfile = file . tempfile
filename = file . original_filename
content_type = file . content_type
end
2015-08-18 11:39:51 +02:00
return { errors : I18n . t ( " upload.file_missing " ) } if tempfile . nil?
2016-06-20 12:35:07 +02:00
# allow users to upload large images that will be automatically reduced to allowed size
2016-04-06 22:51:28 +02:00
max_image_size_kb = SiteSetting . max_image_size_kb . kilobytes
if max_image_size_kb > 0 && FileHelper . is_image? ( filename )
2016-06-20 12:35:07 +02:00
if File . size ( tempfile . path ) > = max_image_size_kb && Upload . should_optimize? ( tempfile . path )
2016-02-22 12:57:24 +01:00
attempt = 2
allow_animation = type == " avatar " ? SiteSetting . allow_animated_avatars : SiteSetting . allow_animated_thumbnails
while attempt > 0
downsized_size = File . size ( tempfile . path )
2016-06-20 12:35:07 +02:00
break if downsized_size < max_image_size_kb
2016-02-22 12:57:24 +01:00
image_info = FastImage . new ( tempfile . path ) rescue nil
w , h = * ( image_info . try ( :size ) || [ 0 , 0 ] )
break if w == 0 || h == 0
2016-06-20 12:35:07 +02:00
downsize_ratio = best_downsize_ratio ( downsized_size , max_image_size_kb )
dimensions = " #{ ( w * downsize_ratio ) . floor } x #{ ( h * downsize_ratio ) . floor } "
2016-02-22 12:57:24 +01:00
OptimizedImage . downsize ( tempfile . path , tempfile . path , dimensions , filename : filename , allow_animation : allow_animation )
attempt -= 1
end
2015-08-12 18:33:13 +02:00
end
end
2015-08-17 18:57:28 +02:00
upload = Upload . create_for ( current_user . id , tempfile , filename , File . size ( tempfile . path ) , content_type : content_type , image_type : type )
2015-06-15 16:12:15 +02:00
if upload . errors . empty? && current_user . admin?
retain_hours = params [ :retain_hours ] . to_i
upload . update_columns ( retain_hours : retain_hours ) if retain_hours > 0
end
if upload . errors . empty? && FileHelper . is_image? ( filename )
Jobs . enqueue ( :create_thumbnails , upload_id : upload . id , type : type , user_id : params [ :user_id ] )
end
upload . errors . empty? ? upload : { errors : upload . errors . values . flatten }
ensure
tempfile . try ( :close! ) rescue nil
end
end
2016-06-20 12:35:07 +02:00
def best_downsize_ratio ( downsized_size , max_image_size )
if downsized_size / 9 > max_image_size
0 . 3
elsif downsized_size / 3 > max_image_size
0 . 6
else
0 . 8
end
end
2013-02-05 14:16:51 -05:00
end