attachments_controller.rb
206 lines
| 6.5 KiB
| text/x-ruby
|
RubyLexer
|
r2114 | # Redmine - project management software | ||
|
r14856 | # Copyright (C) 2006-2016 Jean-Philippe Lang | ||
|
r538 | # | ||
# This program is free software; you can redistribute it and/or | ||||
# modify it under the terms of the GNU General Public License | ||||
# as published by the Free Software Foundation; either version 2 | ||||
# of the License, or (at your option) any later version. | ||||
|
r5716 | # | ||
|
r538 | # This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
|
r5716 | # | ||
|
r538 | # You should have received a copy of the GNU General Public License | ||
# along with this program; if not, write to the Free Software | ||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
class AttachmentsController < ApplicationController | ||||
|
r13283 | before_filter :find_attachment, :only => [:show, :download, :thumbnail, :destroy] | ||
before_filter :find_editable_attachments, :only => [:edit, :update] | ||||
|
r9750 | before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail] | ||
|
r2114 | before_filter :delete_authorize, :only => :destroy | ||
|
r8808 | before_filter :authorize_global, :only => :upload | ||
|
r5716 | |||
|
r15495 | # Disable check for same origin requests for JS files, i.e. attachments with | ||
# MIME type text/javascript. | ||||
skip_after_filter :verify_same_origin_request, :only => :download | ||||
|
r14950 | accept_api_auth :show, :download, :thumbnail, :upload, :destroy | ||
|
r5716 | |||
|
r1502 | def show | ||
|
r6175 | respond_to do |format| | ||
format.html { | ||||
if @attachment.is_diff? | ||||
|
r14907 | @diff = File.read(@attachment.diskfile, :mode => "rb") | ||
|
r7740 | @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline' | ||
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type) | ||||
|
r8641 | # Save diff type as user preference | ||
if User.current.logged? && @diff_type != User.current.pref[:diff_type] | ||||
User.current.pref[:diff_type] = @diff_type | ||||
User.current.preference.save | ||||
end | ||||
|
r6175 | render :action => 'diff' | ||
elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte | ||||
|
r14907 | @content = File.read(@attachment.diskfile, :mode => "rb") | ||
|
r6175 | render :action => 'file' | ||
|
r14942 | elsif @attachment.is_image? | ||
render :action => 'image' | ||||
|
r6175 | else | ||
|
r15016 | render :action => 'other' | ||
|
r6175 | end | ||
} | ||||
format.api | ||||
|
r1502 | end | ||
end | ||||
|
r5716 | |||
|
r538 | def download | ||
|
r2207 | if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project) | ||
@attachment.increment_download | ||||
end | ||||
|
r5716 | |||
|
r9780 | if stale?(:etag => @attachment.digest) | ||
# images are sent inline | ||||
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename), | ||||
:type => detect_content_type(@attachment), | ||||
|
r15027 | :disposition => disposition(@attachment) | ||
|
r9780 | end | ||
|
r538 | end | ||
|
r5716 | |||
|
r9750 | def thumbnail | ||
|
r12901 | if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size]) | ||
if stale?(:etag => tbnail) | ||||
send_file tbnail, | ||||
|
r9780 | :filename => filename_for_content_disposition(@attachment.filename), | ||
:type => detect_content_type(@attachment), | ||||
:disposition => 'inline' | ||||
end | ||||
|
r9750 | else | ||
# No thumbnail for the attachment or thumbnail could not be created | ||||
render :nothing => true, :status => 404 | ||||
end | ||||
end | ||||
|
r8808 | def upload | ||
# Make sure that API users get used to set this content type | ||||
# as it won't trigger Rails' automatic parsing of the request body for parameters | ||||
unless request.content_type == 'application/octet-stream' | ||||
render :nothing => true, :status => 406 | ||||
return | ||||
end | ||||
|
r9469 | @attachment = Attachment.new(:file => request.raw_post) | ||
|
r8808 | @attachment.author = User.current | ||
|
r10467 | @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16) | ||
|
r13406 | @attachment.content_type = params[:content_type].presence | ||
|
r10748 | saved = @attachment.save | ||
|
r8808 | |||
|
r10748 | respond_to do |format| | ||
format.js | ||||
format.api { | ||||
if saved | ||||
render :action => 'upload', :status => :created | ||||
else | ||||
render_validation_errors(@attachment) | ||||
end | ||||
} | ||||
|
r8808 | end | ||
end | ||||
|
r13283 | def edit | ||
end | ||||
def update | ||||
if params[:attachments].is_a?(Hash) | ||||
if Attachment.update_attachments(@attachments, params[:attachments]) | ||||
redirect_back_or_default home_path | ||||
return | ||||
end | ||||
end | ||||
render :action => 'edit' | ||||
end | ||||
|
r2114 | def destroy | ||
|
r8953 | if @attachment.container.respond_to?(:init_journal) | ||
@attachment.container.init_journal(User.current) | ||||
end | ||||
|
r10748 | if @attachment.container | ||
# Make sure association callbacks are called | ||||
@attachment.container.attachments.delete(@attachment) | ||||
else | ||||
@attachment.destroy | ||||
end | ||||
respond_to do |format| | ||||
format.html { redirect_to_referer_or project_path(@project) } | ||||
format.js | ||||
|
r14950 | format.api { render_api_ok } | ||
|
r10748 | end | ||
|
r2114 | end | ||
|
r5716 | |||
|
r13283 | private | ||
def find_attachment | ||||
|
r538 | @attachment = Attachment.find(params[:id]) | ||
|
r1669 | # Show 404 if the filename in the url is wrong | ||
raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename | ||||
|
r538 | @project = @attachment.project | ||
|
r1668 | rescue ActiveRecord::RecordNotFound | ||
render_404 | ||||
|
r538 | end | ||
|
r5716 | |||
|
r13283 | def find_editable_attachments | ||
klass = params[:object_type].to_s.singularize.classify.constantize rescue nil | ||||
unless klass && klass.reflect_on_association(:attachments) | ||||
render_404 | ||||
return | ||||
end | ||||
@container = klass.find(params[:object_id]) | ||||
if @container.respond_to?(:visible?) && !@container.visible? | ||||
render_403 | ||||
return | ||||
end | ||||
@attachments = @container.attachments.select(&:editable?) | ||||
if @container.respond_to?(:project) | ||||
@project = @container.project | ||||
end | ||||
render_404 if @attachments.empty? | ||||
rescue ActiveRecord::RecordNotFound | ||||
render_404 | ||||
end | ||||
|
r2600 | # Checks that the file exists and is readable | ||
def file_readable | ||||
|
r10854 | if @attachment.readable? | ||
true | ||||
else | ||||
logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable." | ||||
render_404 | ||||
end | ||||
|
r2600 | end | ||
|
r5716 | |||
|
r2114 | def read_authorize | ||
@attachment.visible? ? true : deny_access | ||||
end | ||||
|
r5716 | |||
|
r2114 | def delete_authorize | ||
@attachment.deletable? ? true : deny_access | ||||
end | ||||
|
r5716 | |||
|
r3144 | def detect_content_type(attachment) | ||
content_type = attachment.content_type | ||||
|
r13652 | if content_type.blank? || content_type == "application/octet-stream" | ||
|
r3144 | content_type = Redmine::MimeType.of(attachment.filename) | ||
end | ||||
|
r3167 | content_type.to_s | ||
|
r3144 | end | ||
|
r15027 | |||
def disposition(attachment) | ||||
if attachment.is_image? || attachment.is_pdf? | ||||
'inline' | ||||
else | ||||
'attachment' | ||||
end | ||||
end | ||||
|
r538 | end | ||