##// END OF EJS Templates
Returns a 404 error when trying to view/download an attachment that can't be read from disk....
Jean-Philippe Lang -
r2600:15a14e55cdb9
parent child
Show More
@@ -1,74 +1,79
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AttachmentsController < ApplicationController
18 class AttachmentsController < ApplicationController
19 before_filter :find_project
19 before_filter :find_project
20 before_filter :read_authorize, :except => :destroy
20 before_filter :file_readable, :read_authorize, :except => :destroy
21 before_filter :delete_authorize, :only => :destroy
21 before_filter :delete_authorize, :only => :destroy
22
22
23 verify :method => :post, :only => :destroy
23 verify :method => :post, :only => :destroy
24
24
25 def show
25 def show
26 if @attachment.is_diff?
26 if @attachment.is_diff?
27 @diff = File.new(@attachment.diskfile, "rb").read
27 @diff = File.new(@attachment.diskfile, "rb").read
28 render :action => 'diff'
28 render :action => 'diff'
29 elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
29 elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
30 @content = File.new(@attachment.diskfile, "rb").read
30 @content = File.new(@attachment.diskfile, "rb").read
31 render :action => 'file'
31 render :action => 'file'
32 else
32 else
33 download
33 download
34 end
34 end
35 end
35 end
36
36
37 def download
37 def download
38 if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
38 if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
39 @attachment.increment_download
39 @attachment.increment_download
40 end
40 end
41
41
42 # images are sent inline
42 # images are sent inline
43 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
43 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
44 :type => @attachment.content_type,
44 :type => @attachment.content_type,
45 :disposition => (@attachment.image? ? 'inline' : 'attachment')
45 :disposition => (@attachment.image? ? 'inline' : 'attachment')
46
46
47 end
47 end
48
48
49 def destroy
49 def destroy
50 # Make sure association callbacks are called
50 # Make sure association callbacks are called
51 @attachment.container.attachments.delete(@attachment)
51 @attachment.container.attachments.delete(@attachment)
52 redirect_to :back
52 redirect_to :back
53 rescue ::ActionController::RedirectBackError
53 rescue ::ActionController::RedirectBackError
54 redirect_to :controller => 'projects', :action => 'show', :id => @project
54 redirect_to :controller => 'projects', :action => 'show', :id => @project
55 end
55 end
56
56
57 private
57 private
58 def find_project
58 def find_project
59 @attachment = Attachment.find(params[:id])
59 @attachment = Attachment.find(params[:id])
60 # Show 404 if the filename in the url is wrong
60 # Show 404 if the filename in the url is wrong
61 raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
61 raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
62 @project = @attachment.project
62 @project = @attachment.project
63 rescue ActiveRecord::RecordNotFound
63 rescue ActiveRecord::RecordNotFound
64 render_404
64 render_404
65 end
65 end
66
66
67 # Checks that the file exists and is readable
68 def file_readable
69 @attachment.readable? ? true : render_404
70 end
71
67 def read_authorize
72 def read_authorize
68 @attachment.visible? ? true : deny_access
73 @attachment.visible? ? true : deny_access
69 end
74 end
70
75
71 def delete_authorize
76 def delete_authorize
72 @attachment.deletable? ? true : deny_access
77 @attachment.deletable? ? true : deny_access
73 end
78 end
74 end
79 end
@@ -1,152 +1,157
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/md5"
18 require "digest/md5"
19
19
20 class Attachment < ActiveRecord::Base
20 class Attachment < ActiveRecord::Base
21 belongs_to :container, :polymorphic => true
21 belongs_to :container, :polymorphic => true
22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
23
23
24 validates_presence_of :container, :filename, :author
24 validates_presence_of :container, :filename, :author
25 validates_length_of :filename, :maximum => 255
25 validates_length_of :filename, :maximum => 255
26 validates_length_of :disk_filename, :maximum => 255
26 validates_length_of :disk_filename, :maximum => 255
27
27
28 acts_as_event :title => :filename,
28 acts_as_event :title => :filename,
29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
30
30
31 acts_as_activity_provider :type => 'files',
31 acts_as_activity_provider :type => 'files',
32 :permission => :view_files,
32 :permission => :view_files,
33 :author_key => :author_id,
33 :author_key => :author_id,
34 :find_options => {:select => "#{Attachment.table_name}.*",
34 :find_options => {:select => "#{Attachment.table_name}.*",
35 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
35 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
36 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
36 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
37
37
38 acts_as_activity_provider :type => 'documents',
38 acts_as_activity_provider :type => 'documents',
39 :permission => :view_documents,
39 :permission => :view_documents,
40 :author_key => :author_id,
40 :author_key => :author_id,
41 :find_options => {:select => "#{Attachment.table_name}.*",
41 :find_options => {:select => "#{Attachment.table_name}.*",
42 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
42 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
43 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
43 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
44
44
45 cattr_accessor :storage_path
45 cattr_accessor :storage_path
46 @@storage_path = "#{RAILS_ROOT}/files"
46 @@storage_path = "#{RAILS_ROOT}/files"
47
47
48 def validate
48 def validate
49 if self.filesize > Setting.attachment_max_size.to_i.kilobytes
49 if self.filesize > Setting.attachment_max_size.to_i.kilobytes
50 errors.add(:base, :too_long, :count => Setting.attachment_max_size.to_i.kilobytes)
50 errors.add(:base, :too_long, :count => Setting.attachment_max_size.to_i.kilobytes)
51 end
51 end
52 end
52 end
53
53
54 def file=(incoming_file)
54 def file=(incoming_file)
55 unless incoming_file.nil?
55 unless incoming_file.nil?
56 @temp_file = incoming_file
56 @temp_file = incoming_file
57 if @temp_file.size > 0
57 if @temp_file.size > 0
58 self.filename = sanitize_filename(@temp_file.original_filename)
58 self.filename = sanitize_filename(@temp_file.original_filename)
59 self.disk_filename = Attachment.disk_filename(filename)
59 self.disk_filename = Attachment.disk_filename(filename)
60 self.content_type = @temp_file.content_type.to_s.chomp
60 self.content_type = @temp_file.content_type.to_s.chomp
61 self.filesize = @temp_file.size
61 self.filesize = @temp_file.size
62 end
62 end
63 end
63 end
64 end
64 end
65
65
66 def file
66 def file
67 nil
67 nil
68 end
68 end
69
69
70 # Copies the temporary file to its final location
70 # Copies the temporary file to its final location
71 # and computes its MD5 hash
71 # and computes its MD5 hash
72 def before_save
72 def before_save
73 if @temp_file && (@temp_file.size > 0)
73 if @temp_file && (@temp_file.size > 0)
74 logger.debug("saving '#{self.diskfile}'")
74 logger.debug("saving '#{self.diskfile}'")
75 md5 = Digest::MD5.new
75 md5 = Digest::MD5.new
76 File.open(diskfile, "wb") do |f|
76 File.open(diskfile, "wb") do |f|
77 buffer = ""
77 buffer = ""
78 while (buffer = @temp_file.read(8192))
78 while (buffer = @temp_file.read(8192))
79 f.write(buffer)
79 f.write(buffer)
80 md5.update(buffer)
80 md5.update(buffer)
81 end
81 end
82 end
82 end
83 self.digest = md5.hexdigest
83 self.digest = md5.hexdigest
84 end
84 end
85 # Don't save the content type if it's longer than the authorized length
85 # Don't save the content type if it's longer than the authorized length
86 if self.content_type && self.content_type.length > 255
86 if self.content_type && self.content_type.length > 255
87 self.content_type = nil
87 self.content_type = nil
88 end
88 end
89 end
89 end
90
90
91 # Deletes file on the disk
91 # Deletes file on the disk
92 def after_destroy
92 def after_destroy
93 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
93 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
94 end
94 end
95
95
96 # Returns file's location on disk
96 # Returns file's location on disk
97 def diskfile
97 def diskfile
98 "#{@@storage_path}/#{self.disk_filename}"
98 "#{@@storage_path}/#{self.disk_filename}"
99 end
99 end
100
100
101 def increment_download
101 def increment_download
102 increment!(:downloads)
102 increment!(:downloads)
103 end
103 end
104
104
105 def project
105 def project
106 container.project
106 container.project
107 end
107 end
108
108
109 def visible?(user=User.current)
109 def visible?(user=User.current)
110 container.attachments_visible?(user)
110 container.attachments_visible?(user)
111 end
111 end
112
112
113 def deletable?(user=User.current)
113 def deletable?(user=User.current)
114 container.attachments_deletable?(user)
114 container.attachments_deletable?(user)
115 end
115 end
116
116
117 def image?
117 def image?
118 self.filename =~ /\.(jpe?g|gif|png)$/i
118 self.filename =~ /\.(jpe?g|gif|png)$/i
119 end
119 end
120
120
121 def is_text?
121 def is_text?
122 Redmine::MimeType.is_type?('text', filename)
122 Redmine::MimeType.is_type?('text', filename)
123 end
123 end
124
124
125 def is_diff?
125 def is_diff?
126 self.filename =~ /\.(patch|diff)$/i
126 self.filename =~ /\.(patch|diff)$/i
127 end
127 end
128
128
129 # Returns true if the file is readable
130 def readable?
131 File.readable?(diskfile)
132 end
133
129 private
134 private
130 def sanitize_filename(value)
135 def sanitize_filename(value)
131 # get only the filename, not the whole path
136 # get only the filename, not the whole path
132 just_filename = value.gsub(/^.*(\\|\/)/, '')
137 just_filename = value.gsub(/^.*(\\|\/)/, '')
133 # NOTE: File.basename doesn't work right with Windows paths on Unix
138 # NOTE: File.basename doesn't work right with Windows paths on Unix
134 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
139 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
135
140
136 # Finally, replace all non alphanumeric, hyphens or periods with underscore
141 # Finally, replace all non alphanumeric, hyphens or periods with underscore
137 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
142 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
138 end
143 end
139
144
140 # Returns an ASCII or hashed filename
145 # Returns an ASCII or hashed filename
141 def self.disk_filename(filename)
146 def self.disk_filename(filename)
142 df = DateTime.now.strftime("%y%m%d%H%M%S") + "_"
147 df = DateTime.now.strftime("%y%m%d%H%M%S") + "_"
143 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
148 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
144 df << filename
149 df << filename
145 else
150 else
146 df << Digest::MD5.hexdigest(filename)
151 df << Digest::MD5.hexdigest(filename)
147 # keep the extension if any
152 # keep the extension if any
148 df << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
153 df << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
149 end
154 end
150 df
155 df
151 end
156 end
152 end
157 end
@@ -1,137 +1,142
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'attachments_controller'
19 require 'attachments_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class AttachmentsController; def rescue_action(e) raise e end; end
22 class AttachmentsController; def rescue_action(e) raise e end; end
23
23
24
24
25 class AttachmentsControllerTest < Test::Unit::TestCase
25 class AttachmentsControllerTest < Test::Unit::TestCase
26 fixtures :users, :projects, :roles, :members, :enabled_modules, :issues, :attachments,
26 fixtures :users, :projects, :roles, :members, :enabled_modules, :issues, :trackers, :attachments,
27 :versions, :wiki_pages, :wikis
27 :versions, :wiki_pages, :wikis, :documents
28
28
29 def setup
29 def setup
30 @controller = AttachmentsController.new
30 @controller = AttachmentsController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 Attachment.storage_path = "#{RAILS_ROOT}/test/fixtures/files"
33 Attachment.storage_path = "#{RAILS_ROOT}/test/fixtures/files"
34 User.current = nil
34 User.current = nil
35 end
35 end
36
36
37 def test_routing
37 def test_routing
38 assert_routing('/attachments/1', :controller => 'attachments', :action => 'show', :id => '1')
38 assert_routing('/attachments/1', :controller => 'attachments', :action => 'show', :id => '1')
39 assert_routing('/attachments/1/filename.ext', :controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext')
39 assert_routing('/attachments/1/filename.ext', :controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext')
40 assert_routing('/attachments/download/1', :controller => 'attachments', :action => 'download', :id => '1')
40 assert_routing('/attachments/download/1', :controller => 'attachments', :action => 'download', :id => '1')
41 assert_routing('/attachments/download/1/filename.ext', :controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext')
41 assert_routing('/attachments/download/1/filename.ext', :controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext')
42 end
42 end
43
43
44 def test_recognizes
44 def test_recognizes
45 assert_recognizes({:controller => 'attachments', :action => 'show', :id => '1'}, '/attachments/1')
45 assert_recognizes({:controller => 'attachments', :action => 'show', :id => '1'}, '/attachments/1')
46 assert_recognizes({:controller => 'attachments', :action => 'show', :id => '1'}, '/attachments/show/1')
46 assert_recognizes({:controller => 'attachments', :action => 'show', :id => '1'}, '/attachments/show/1')
47 assert_recognizes({:controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext'}, '/attachments/1/filename.ext')
47 assert_recognizes({:controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext'}, '/attachments/1/filename.ext')
48 assert_recognizes({:controller => 'attachments', :action => 'download', :id => '1'}, '/attachments/download/1')
48 assert_recognizes({:controller => 'attachments', :action => 'download', :id => '1'}, '/attachments/download/1')
49 assert_recognizes({:controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext'},'/attachments/download/1/filename.ext')
49 assert_recognizes({:controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext'},'/attachments/download/1/filename.ext')
50 end
50 end
51
51
52 def test_show_diff
52 def test_show_diff
53 get :show, :id => 5
53 get :show, :id => 5
54 assert_response :success
54 assert_response :success
55 assert_template 'diff'
55 assert_template 'diff'
56 assert_equal 'text/html', @response.content_type
56 assert_equal 'text/html', @response.content_type
57 end
57 end
58
58
59 def test_show_text_file
59 def test_show_text_file
60 get :show, :id => 4
60 get :show, :id => 4
61 assert_response :success
61 assert_response :success
62 assert_template 'file'
62 assert_template 'file'
63 assert_equal 'text/html', @response.content_type
63 assert_equal 'text/html', @response.content_type
64 end
64 end
65
65
66 def test_show_text_file_should_send_if_too_big
66 def test_show_text_file_should_send_if_too_big
67 Setting.file_max_size_displayed = 512
67 Setting.file_max_size_displayed = 512
68 Attachment.find(4).update_attribute :filesize, 754.kilobyte
68 Attachment.find(4).update_attribute :filesize, 754.kilobyte
69
69
70 get :show, :id => 4
70 get :show, :id => 4
71 assert_response :success
71 assert_response :success
72 assert_equal 'application/x-ruby', @response.content_type
72 assert_equal 'application/x-ruby', @response.content_type
73 end
73 end
74
74
75 def test_show_other
75 def test_show_other
76 get :show, :id => 6
76 get :show, :id => 6
77 assert_response :success
77 assert_response :success
78 assert_equal 'application/octet-stream', @response.content_type
78 assert_equal 'application/octet-stream', @response.content_type
79 end
79 end
80
80
81 def test_download_text_file
81 def test_download_text_file
82 get :download, :id => 4
82 get :download, :id => 4
83 assert_response :success
83 assert_response :success
84 assert_equal 'application/x-ruby', @response.content_type
84 assert_equal 'application/x-ruby', @response.content_type
85 end
85 end
86
86
87 def test_download_missing_file
88 get :download, :id => 2
89 assert_response 404
90 end
91
87 def test_anonymous_on_private_private
92 def test_anonymous_on_private_private
88 get :download, :id => 7
93 get :download, :id => 7
89 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fdownload%2F7'
94 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fdownload%2F7'
90 end
95 end
91
96
92 def test_destroy_issue_attachment
97 def test_destroy_issue_attachment
93 issue = Issue.find(3)
98 issue = Issue.find(3)
94 @request.session[:user_id] = 2
99 @request.session[:user_id] = 2
95
100
96 assert_difference 'issue.attachments.count', -1 do
101 assert_difference 'issue.attachments.count', -1 do
97 post :destroy, :id => 1
102 post :destroy, :id => 1
98 end
103 end
99 # no referrer
104 # no referrer
100 assert_redirected_to 'projects/ecookbook'
105 assert_redirected_to 'projects/ecookbook'
101 assert_nil Attachment.find_by_id(1)
106 assert_nil Attachment.find_by_id(1)
102 j = issue.journals.find(:first, :order => 'created_on DESC')
107 j = issue.journals.find(:first, :order => 'created_on DESC')
103 assert_equal 'attachment', j.details.first.property
108 assert_equal 'attachment', j.details.first.property
104 assert_equal '1', j.details.first.prop_key
109 assert_equal '1', j.details.first.prop_key
105 assert_equal 'error281.txt', j.details.first.old_value
110 assert_equal 'error281.txt', j.details.first.old_value
106 end
111 end
107
112
108 def test_destroy_wiki_page_attachment
113 def test_destroy_wiki_page_attachment
109 @request.session[:user_id] = 2
114 @request.session[:user_id] = 2
110 assert_difference 'Attachment.count', -1 do
115 assert_difference 'Attachment.count', -1 do
111 post :destroy, :id => 3
116 post :destroy, :id => 3
112 assert_response 302
117 assert_response 302
113 end
118 end
114 end
119 end
115
120
116 def test_destroy_project_attachment
121 def test_destroy_project_attachment
117 @request.session[:user_id] = 2
122 @request.session[:user_id] = 2
118 assert_difference 'Attachment.count', -1 do
123 assert_difference 'Attachment.count', -1 do
119 post :destroy, :id => 8
124 post :destroy, :id => 8
120 assert_response 302
125 assert_response 302
121 end
126 end
122 end
127 end
123
128
124 def test_destroy_version_attachment
129 def test_destroy_version_attachment
125 @request.session[:user_id] = 2
130 @request.session[:user_id] = 2
126 assert_difference 'Attachment.count', -1 do
131 assert_difference 'Attachment.count', -1 do
127 post :destroy, :id => 9
132 post :destroy, :id => 9
128 assert_response 302
133 assert_response 302
129 end
134 end
130 end
135 end
131
136
132 def test_destroy_without_permission
137 def test_destroy_without_permission
133 post :destroy, :id => 3
138 post :destroy, :id => 3
134 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fdestroy%2F3'
139 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fdestroy%2F3'
135 assert Attachment.find_by_id(3)
140 assert Attachment.find_by_id(3)
136 end
141 end
137 end
142 end
General Comments 0
You need to be logged in to leave comments. Login now