#encoding: utf-8 # Redmine - project management software # Copyright (C) 2006-2013 Jean-Philippe Lang # # 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. # # 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. # # 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 layout "users_base" before_filter :verify_authenticity_token, only: [:upload] before_filter :find_project, :only => [:show, :download, :thumbnail, :destroy, :delete_homework]#, :except => [:upload, :autocomplete] before_filter :file_readable, :read_authorize, :only => [:show, :thumbnail]#Modified by young before_filter :delete_authorize, :only => [:destroy] before_filter :authorize_global, :only => [:upload] before_filter :authorize_attachment_download1, :only => [:download] before_filter :has_login #before_filter :login_without_softapplication, only: [:download] accept_api_auth :show, :download, :upload require 'iconv' include AttachmentsHelper include ApplicationHelper def show respond_to do |format| format.html { if @attachment.is_diff? @diff = File.new(@attachment.diskfile, "rb").read @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline' @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type) # 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 render :action => 'diff' elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte @content = File.new(@attachment.diskfile, "rb").read # 编码为非 UTF-8先进行间接转码 # 部分unicode编码不直接支持转为 UTF-8 # modify by nwb if @content.encoding.name != 'UTF-8' @content = @content.force_encoding('GBK') @content = @content.encode('UTF-8') end render :action => 'file' else download end } format.api end rescue Encoding::InvalidByteSequenceError => e render :action => 'file' end def pdf?(file) file.downcase.end_with?(".pdf") end def direct_download @attachment.increment_download file_type = detect_content_type(@attachment) send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename), :type => file_type, :disposition => 'attachment' #inline can open in browser end def direct_download_history @attachment_history = AttachmentHistory.find(params[:id]) @attachment_history.increment_download send_file @attachment_history.diskfile_history, :filename => filename_for_content_disposition(@attachment_history.filename), :type => detect_content_type(@attachment_history), :disposition => 'attachment' #inline can open in browser end def download_history @attachment_history = AttachmentHistory.find(params[:id]) candown = attachment_history_candown @attachment_history if candown || User.current.admin? || User.current.id == @attachment_history.author_id if stale?(:etag => @attachment_history.digest) if params[:preview] == 'true' convered_file = @attachment_history.diskfile_history #如果本身不是pdf文件,则先寻找是不是已转换化,如果没有则转化 unless pdf?(convered_file) convered_file = File.join(Rails.root, "files", "convered_office", @attachment.disk_filename + ".pdf") unless File.exist?(convered_file) office = Trustie::Utils::Office.new(@attachment_history.diskfile) office.conver(convered_file) end end if File.exist?(convered_file) && pdf?(convered_file) send_file convered_file, :type => 'application/pdf; charset=utf-8', :disposition => 'inline' else direct_download_history end else # 记录用户行为 record_user_actions(params[:id]) # 直接下载历史版本 direct_download_history end end else render_403 :message => :notice_not_authorized end rescue => e redirect_to "http://" + (Setting.host_name.to_s) +"/file_not_found.html" end def record_user_actions id if params[:action] == "download_history" UserActions.create(:action_id => id, :action_type => "AttachmentHistory", :user_id => User.current.id) unless id.nil? elsif params[:action] == "download" UserActions.create(:action_id => id, :action_type => "Attachment", :user_id => User.current.id) unless id.nil? end end def download # modify by nwb # 下载添加权限设置 candown = attachment_candown @attachment if candown || User.current.admin? || User.current.id == @attachment.author_id if stale?(:etag => @attachment.digest) if params[:preview] == 'true' convered_file = @attachment.diskfile #如果本身不是pdf文件,则先寻找是不是已转换化,如果没有则转化 unless pdf?(convered_file) convered_file = File.join(Rails.root, "files", "convered_office", @attachment.disk_filename + ".pdf") unless File.exist?(convered_file) office = Trustie::Utils::Office.new(@attachment.diskfile) office.conver(convered_file) end end if File.exist?(convered_file) && pdf?(convered_file) send_file convered_file, :type => 'application/pdf; charset=utf-8', :disposition => 'inline' else direct_download end else # 记录用户行为 record_user_actions(params[:id]) direct_download end end else render_403 :message => :notice_not_authorized end rescue => e redirect_to "http://" + (Setting.host_name.to_s) +"/file_not_found.html" end #更新资源文件类型 def updateType @attachment = Attachment.find(params[:attachmentid]) if @attachment != nil @attachment.attachtype = params[:newtype] @attachment.save render :text =>'success' else render :text=>'error' end end # 更新文件密级 def updateFileDense @attachment = Attachment.find(params[:attachmentid]) if @attachment != nil filedense = params[:newtype].to_s # d = Iconv.conv("unicodebig","utf-8",filedense) if filedense == "%E5%85%AC%E5%BC%80" #l(:field_is_public) @attachment.is_public = 1 else @attachment.is_public = 0 end @attachment.save @newfiledense = filedense end respond_to do |format| format.js end end def update_file_dense @attachment = Attachment.find(params[:attachmentid]) if @attachment != nil filedense = params[:newtype].to_s if filedense == "1" @attachment.is_public = 1 else @attachment.is_public = 0 end @attachment.save @newfiledense = filedense end if @attachment.container_type == "Project" || @attachment.container_type == "Course" tip_attachment_update end respond_to do |format| format.js end end def tip_attachment_update if params[:course_id] @tip_all_attachments = Attachment.where(:container_type => "Course", :container_id => params[:course_id]) @tip_all_public_attachments = Attachment.where(:container_type => "Course", :container_id => params[:course_id], :is_public => 1) @tip_all_private_attachments = Attachment.where(:container_type => "Course", :container_id => params[:course_id], :is_public => 0) @course = Course.find(params[:course_id]) elsif params[:project_id] @tip_all_attachments = Attachment.where(:container_type => "Project", :container_id => params[:project_id]) @tip_all_public_attachments = Attachment.where(:container_type => "Project", :container_id => params[:project_id], :is_public => 1) @tip_all_private_attachments = Attachment.where(:container_type => "Project", :container_id => params[:project_id], :is_public => 0) @project = Project.find(params[:project_id]) end @tag_name = params[:tag_name] @other = params[:other] unless @tag_name.blank? if @other if @project @tip_all_attachments = @tip_all_attachments.select{|attachment| !attachment.tag_list.include?('软件版本') && !attachment.tag_list.include?('文档') && !attachment.tag_list.include?('代码') && !attachment.tag_list.include?('媒体') && !attachment.tag_list.include?('论文') } @tip_all_public_attachments = @tip_all_public_attachments.select{|attachment| !attachment.tag_list.include?('软件版本') && !attachment.tag_list.include?('文档') && !attachment.tag_list.include?('代码') && !attachment.tag_list.include?('媒体') && !attachment.tag_list.include?('论文') } @tip_all_private_attachments = @tip_all_private_attachments.select{|attachment| !attachment.tag_list.include?('软件版本') && !attachment.tag_list.include?('文档') && !attachment.tag_list.include?('代码') && !attachment.tag_list.include?('媒体') && !attachment.tag_list.include?('论文') } elsif @course @tip_all_attachments = @tip_all_attachments.select{|attachment| !attachment.tag_list.include?('课件') && !attachment.tag_list.include?('软件') && !attachment.tag_list.include?('媒体') && !attachment.tag_list.include?('代码') && !attachment.tag_list.include?('论文') } @tip_all_public_attachments = @tip_all_public_attachments.select{|attachment| !attachment.tag_list.include?('课件') && !attachment.tag_list.include?('软件') && !attachment.tag_list.include?('媒体') && !attachment.tag_list.include?('代码') && !attachment.tag_list.include?('论文') } @tip_all_private_attachments = @tip_all_private_attachments.select{|attachment| !attachment.tag_list.include?('课件') && !attachment.tag_list.include?('软件') && !attachment.tag_list.include?('媒体') && !attachment.tag_list.include?('代码') && !attachment.tag_list.include?('论文') } end else @tip_all_attachments = @tip_all_attachments.select{|attachment| attachment.tag_list.include?(@tag_name)} @tip_all_public_attachments = @tip_all_public_attachments.select{|attachment| attachment.tag_list.include?(@tag_name)} @tip_all_private_attachments = @tip_all_private_attachments.select{|attachment| attachment.tag_list.include?(@tag_name)} end end @tip_all_attachments = @tip_all_attachments.count @tip_all_public_attachments = @tip_all_public_attachments.count @tip_all_private_attachments = @tip_all_private_attachments.count end def thumbnail if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size]) if stale?(:etag => thumbnail) send_file thumbnail, :filename => filename_for_content_disposition(@attachment.filename), :type => detect_content_type(@attachment), :disposition => 'inline' end else # No thumbnail for the attachment or thumbnail could not be created render :nothing => true, :status => 404 end end 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 @attachment = Attachment.new(:file => request.raw_post) @attachment.author = User.current if !params[:project].nil? @attachment.container_type = 'Project' @attachment.container_id = params[:project].split("?")[0] end @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16) saved = @attachment.save respond_to do |format| format.js format.api { if saved render :action => 'upload', :status => :created else render_validation_errors(@attachment) end } end end def upload_attachment_version @flag = false Attachment.transaction do @old_attachment = Attachment.find params[:old_attachment_id] #取出当前上传的文件 @attachment = Attachment.find(params[:attachments ].first[1][:attachment_id]) #将需要修改的记录保存到历史记录 @history = AttachmentHistory.new @history.attributes = @old_attachment.attributes.dup.except("id") @history.attachment_id = params[:old_attachment_id] #需要更新版本号,需要拿到原来该文件最大的历史版本号 @old_history = @old_attachment.attachment_histories.reorder('version desc').first @history.version = @old_history.nil? ? 1 : @old_history.version + 1 @history.save #历史记录保存完毕 #将最新保存的记录 数据替换到 需要修改的文件记录 @old_attachment.attributes = @attachment.attributes.dup.except("id","container_id","container_type","is_public","downloads", "quotes") # 如果附件描述被修改,则保存附件 unless params[:description] == @attachment.description @old_attachment.description = params[:description] end @old_attachment.save #删除当前记录 @attachment.delete @flag = true end # tip_attachment_update respond_to do |format| format.js end end # prams[:type] => history 历史版本 def destroy if params[:type] == "history" begin AttachmentHistory.find(params[:history_id]).destroy @attachment = Attachment.find(params[:id]) @is_history = true @is_history_destroy = false @attachment_histories = @attachment.attachment_histories rescue Exception => e puts e end elsif params[:type] == "history_delete" begin AttachmentHistory.find(params[:history_id]).destroy @attachment = Attachment.find(params[:id]) @is_history_delete = true @is_history_destroy = false @attachment_histories = @attachment.attachment_histories @attachment_histories_count = @attachment_histories.count rescue Exception => e puts e end else @history = params[:history] if @attachment.container.respond_to?(:init_journal) @attachment.container.init_journal(User.current) end if @attachment.container if @attachment.container_type == "Issue" @attachment.destroy else @attachment.container.attachments.delete(@attachment) end else @attachment.destroy end end respond_to do |format| if !@attachment.container.nil? && (@attachment.container.is_a?(Course) || ((@attachment.container.has_attribute?(:course) || @attachment.container.has_attribute?(:course_id) ) && @attachment.container.course ) || ((@attachment.container.has_attribute?(:board) || @attachment.container.has_attribute?(:board_id)) && @attachment.container.board && @attachment.container.board.course ) || @attachment.container.is_a?(StudentWorksScore) || @attachment.container.is_a?(HomeworkCommon) || @attachment.container.is_a?(StudentWork)) if @attachment.container.is_a?(News) format.html { redirect_to_referer_or news_path(@attachment.container) } elsif @attachment.container.is_a?(StudentWorksScore) @is_destroy = true unless params[:attachment_id] #根据ID删除页面对应的数据,js刷新页面 format.js elsif @attachment.container.is_a?(HomeworkCommon) @is_destroy = true unless params[:attachment_id] #根据ID删除页面对应的数据,js刷新页面 format.js elsif @attachment.container.is_a?(StudentWork) @is_destroy = true unless params[:attachment_id] #根据ID删除页面对应的数据,js刷新页面 format.js elsif @attachment.container.is_a?(Message) format.html { redirect_to_referer_or new_board_message_path(@attachment.container) } elsif @attachment.container.is_a?(BlogComment) format.html { redirect_to_referer_or user_blog_blog_comment_path(:user_id=>@attachment.container.author.id,:blog_id=>@attachment.container.blog_id,:id=>@attachment.container.id)} elsif @course.nil? format.html { redirect_to_referer_or forum_memo_path(@attachment.container.forum, @attachment.container) } else format.html { redirect_to_referer_or course_path(@course) } end elsif !@attachment.container.nil? && @attachment.container.is_a?(Softapplication) format.html { redirect_to_referer_or softapplications_path(@attachment.container) } elsif !@attachment.container.nil? && @attachment.container.is_a?(Bid) format.html { redirect_to_referer_or respond_path(@attachment.container) } elsif !@attachment.container.nil? && @attachment.container.is_a?(PhoneAppVersion) format.html { redirect_to_referer_or mobile_version_path } elsif !@attachment.container.nil? && @attachment.container.is_a?(OrgSubfield) format.html {redirect_to_referer_or org_subfield_files_path(@attachment.container)} elsif !@attachment.container.nil? && @attachment.container.is_a?(OrgDocumentComment) format.html {redirect_to_referer_or org_document_comment_path(@attachment.container)} else if @project.nil? format.html { redirect_to_referer_or forum_memo_path(@attachment.container.forum, @attachment.container) } else format.html { redirect_to_referer_or project_path(@project) } end end format.js end end def delete_homework @bid = @attachment.container.bid # Make sure association callbacks are called container = @attachment.container @attachment.container.attachments.delete(@attachment) #if container.attachments.empty? #container.delete #end respond_to do |format| format.html { redirect_to_referer_or respond_path(@bid) } format.js end end #删除竞赛作品的附件 def delete_softapplications @attachment = Attachment.find params[:id] @softapplication = @attachment.container if @attachment!=nil @attachment.container.attachments.delete(@attachment) if @attachment!=nil respond_to do |format| format.html { redirect_to_referer_or edit_softapplication_path(@softapplication) } #format.js end end def autocomplete # modify by nwb if params[:project_id] @project = Project.find_by_id(params[:project_id]) elsif params[:course_id] @course = Course.find_by_id(params[:course_id]) end respond_to do |format| format.js end end def add_exist_file_to_project classname = params[:class_name] class_id = params[:class_id] attachments = params[:attachment][:attach] obj = Object.const_get(classname).find_by_id(class_id) attachments.collect do |attach_id| ori = Attachment.find_by_id(attach_id) next if ori.blank? attach_copied_obj = ori.copy attach_copied_obj.tag_list.add(ori.tag_list) # tag关联 attach_copied_obj.container = obj attach_copied_obj.created_on = Time.now attach_copied_obj.author_id = User.current.id if attach_copied_obj.attachtype == nil attach_copied_obj.attachtype = 1 end @obj = obj @save_flag = attach_copied_obj.save @save_message = attach_copied_obj.errors.full_messages end respond_to do |format| format.js end rescue NoMethodError @save_flag = false @save_message = [] << l(:error_attachment_empty) respond_to do |format| format.js end end def add_exist_file_to_course class_id = params[:class_id] attachments = params[:attachment][:attach] obj = Course.find_by_id(class_id) attachments.collect do |attach_id| ori = Attachment.find_by_id(attach_id) next if ori.blank? attach_copied_obj = ori.copy attach_copied_obj.tag_list.add(ori.tag_list) # tag关联 attach_copied_obj.container = obj attach_copied_obj.created_on = Time.now attach_copied_obj.author_id = User.current.id if attach_copied_obj.attachtype == nil attach_copied_obj.attachtype = 4 end @obj = obj @save_flag = attach_copied_obj.save @save_message = attach_copied_obj.errors.full_messages end respond_to do |format| format.js end rescue NoMethodError @save_flag = false @save_message = [] << l(:error_attachment_empty) respond_to do |format| format.js end end def add_exist_file_to_projects file = Attachment.find(params[:file_id]) projects = params[:projects][:project] @message = "" projects.each do |project| c = Project.find(project); if project_contains_attachment?(c,file) if @message && @message == "" @message += l(:label_project_prompt) + c.name + l(:label_contain_resource) + file.filename + l(:label_quote_resource_failed) next else @message += "
" + l(:label_project_prompt) + c.name + l(:label_contain_resource) + file.filename + l(:label_quote_resource_failed) next end end attach_copied_obj = file.copy attach_copied_obj.tag_list.add(file.tag_list) # tag关联 attach_copied_obj.container = c attach_copied_obj.created_on = Time.now attach_copied_obj.author_id = User.current.id attach_copied_obj.copy_from = file.copy_from.nil? ? file.id : file.copy_from if attach_copied_obj.attachtype == nil attach_copied_obj.attachtype = 4 end @obj = c @save_flag = attach_copied_obj.save @save_message = attach_copied_obj.errors.full_messages update_quotes attach_copied_obj end respond_to do |format| format.js end rescue NoMethodError @save_flag = false @save_message = [] << l(:label_project_empty_select) respond_to do |format| format.js end end def add_exist_file_to_courses file = Attachment.find(params[:file_id]) courses = params[:courses][:course] @message = "" courses.each do |course| c = Course.find(course); if course_contains_attachment?(c,file) if @message && @message == "" @message += l(:label_course_prompt) + c.name + l(:label_contain_resource) + file.filename + l(:label_quote_resource_failed) next else @message += "
" + l(:label_course_prompt) + c.name + l(:label_contain_resource) + file.filename + l(:label_quote_resource_failed) next end end attach_copied_obj = file.copy attach_copied_obj.tag_list.add(file.tag_list) # tag关联 attach_copied_obj.container = c attach_copied_obj.created_on = Time.now attach_copied_obj.author_id = User.current.id attach_copied_obj.copy_from = file.copy_from.nil? ? file.id : file.copy_from if attach_copied_obj.attachtype == nil attach_copied_obj.attachtype = 4 end @obj = c @save_flag = attach_copied_obj.save @save_message = attach_copied_obj.errors.full_messages update_quotes attach_copied_obj end respond_to do |format| format.js end rescue NoMethodError @save_flag = false @save_message = [] << l(:label_course_empty_select) respond_to do |format| format.js end end def add_exist_file_to_org_subfield file = Attachment.find(params[:file_id]) org_subfields = params[:org_subfields][:org_subfield] @message = "" org_subfields.each do |org_subfield| s = OrgSubfield.find(org_subfield) if s.attachments.include?file if @message && @message == "" @message += l(:label_resource_subfield_prompt) + c.name + l(:label_contain_resource) + file.filename + l(:label_quote_resource_failed) next else @message += "
" + l(:label_resource_subfield_prompt) + c.name + l(:label_contain_resource) + file.filename + l(:label_quote_resource_failed) next end end attach_copied_obj = file.copy attach_copied_obj.tag_list.add(file.tag_list) # tag关联 attach_copied_obj.container = s attach_copied_obj.created_on = Time.now attach_copied_obj.author_id = User.current.id attach_copied_obj.copy_from = file.copy_from.nil? ? file.id : file.copy_from if attach_copied_obj.attachtype == nil attach_copied_obj.attachtype = 4 end @obj = s @save_flag = attach_copied_obj.save @save_message = attach_copied_obj.errors.full_messages update_quotes attach_copied_obj end respond_to do |format| format.js end rescue NoMethodError @save_flag = false @save_message = [] << l(:label_resource_subfield_empty_select) respond_to do |format| format.js end end def update_quotes attachment if attachment.copy_from attachments = Attachment.find_by_sql("select * from attachments where copy_from = #{attachment.copy_from} or id = #{attachment.copy_from}") else attachments = Attachment.find_by_sql("select * from attachments where copy_from = #{attachment.id} or id = #{attachment.copy_from}") end attachment.quotes = get_qute_number attachment attachment.save attachments.each do |att| att.quotes = attachment.quotes att.save end end #找到文件的所有的历史版本 def attachment_versions @history = params[:history] @attachment = Attachment.find(params[:id]) @attachment_histories = @attachment.attachment_histories respond_to do |format| format.js end end def attachment_versions_delete @attachment = Attachment.find(params[:id]) @attachment_histories = @attachment.attachment_histories @attachment_histories_count = @attachment_histories.count respond_to do |format| format.js end end #找到文件的所有的历史版本及当前版本 def attachment_history_download @attachment = Attachment.find(params[:id]) @attachment_histories = @attachment.attachment_histories respond_to do |format| format.js end end private def find_project @attachment = Attachment.find(params[:id]) # Show 404 if the filename in the url is wrong # modify by nwb raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename if @attachment.container_type == 'Course' @course = @attachment.course elsif !@attachment.container.nil? && (@attachment.container.has_attribute?(:course) || @attachment.container.has_attribute?(:course_id)) && @attachment.container.course @course = @attachment.container.course elsif !@attachment.container.nil? && ((@attachment.container.has_attribute?(:board) || @attachment.container.has_attribute?(:board_id)) && @attachment.container.board && @attachment.container.board.course) @course = @attachment.container.board.course elsif !@attachment.container.nil? && ((@attachment.container.has_attribute?(:board) || @attachment.container.has_attribute?(:board_id)) && @attachment.container.board && @attachment.container.board.contest) @contest = @attachment.container.board.contest else unless @attachment.container_type == 'Syllabus' || @attachment.container_type == 'Bid' || @attachment.container_type == 'Organization' || @attachment.container_type == 'HomeworkAttach' || @attachment.container_type == 'Memo' || @attachment.container_type == 'Softapplication' || @attachment.container_type == 'PhoneAppVersion' || @attachment.container_type == 'StudentWorksScore'|| @attachment.container_type == 'StudentWork' || @attachment.container_type == 'Work'|| @attachment.container_type == 'ContestantWork'|| @attachment.container_type == 'Contest' || @attachment.container_type == 'HomeworkBank' @project = @attachment.project end end rescue ActiveRecord::RecordNotFound render_404 end # Checks that the file exists and is readable def file_readable if @attachment.readable? true else logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable." render_404 end end def read_authorize if @attachment.container_type == "HomeworkAttach" || @attachment.container_type == 'Bid' true #User.current.allowed_to?(:view_homework_attaches, @attachment.project) ? true : deny_access else @attachment.visible? ? true : deny_access end end def delete_authorize @attachment.deletable? ? true : deny_access end def detect_content_type(attachment) content_type = attachment.content_type if content_type.blank? content_type = Redmine::MimeType.of(attachment.filename) end content_type.to_s end def login_without_softapplication referer = request.headers['Referer'] require_login unless referer =~ /softapplication/ || @attachment.container_type == "Memo" end def renderTag @attachmentNew = Attachment.find(params[:attchmentId]) respond_to do |format| format.js end end def has_login unless (@attachment && @attachment.container_type == "Organization").nil? unless (@attachment && @attachment.container_type == "PhoneAppVersion").nil? render_403 if (!User.current.logged? && !(params[:type] && params[:type] == "wechat")) && !(@attachment.container_type == 'OrgSubfield' && @attachment.container.organization.allow_guest_download) && !(@attachment.container_type == 'OrgDocumentComment' && @attachment.container.organization.allow_guest_download) end end end end