##// END OF EJS Templates
Send a better content type than application/octet-stream (#19131)....
Jean-Philippe Lang -
r13652:0b04de0a23e2
parent child
Show More
@@ -1,191 +1,191
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class AttachmentsController < ApplicationController
19 19 before_filter :find_attachment, :only => [:show, :download, :thumbnail, :destroy]
20 20 before_filter :find_editable_attachments, :only => [:edit, :update]
21 21 before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
22 22 before_filter :delete_authorize, :only => :destroy
23 23 before_filter :authorize_global, :only => :upload
24 24
25 25 accept_api_auth :show, :download, :thumbnail, :upload
26 26
27 27 def show
28 28 respond_to do |format|
29 29 format.html {
30 30 if @attachment.is_diff?
31 31 @diff = File.new(@attachment.diskfile, "rb").read
32 32 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
33 33 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
34 34 # Save diff type as user preference
35 35 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
36 36 User.current.pref[:diff_type] = @diff_type
37 37 User.current.preference.save
38 38 end
39 39 render :action => 'diff'
40 40 elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
41 41 @content = File.new(@attachment.diskfile, "rb").read
42 42 render :action => 'file'
43 43 else
44 44 download
45 45 end
46 46 }
47 47 format.api
48 48 end
49 49 end
50 50
51 51 def download
52 52 if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
53 53 @attachment.increment_download
54 54 end
55 55
56 56 if stale?(:etag => @attachment.digest)
57 57 # images are sent inline
58 58 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
59 59 :type => detect_content_type(@attachment),
60 60 :disposition => (@attachment.image? ? 'inline' : 'attachment')
61 61 end
62 62 end
63 63
64 64 def thumbnail
65 65 if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size])
66 66 if stale?(:etag => tbnail)
67 67 send_file tbnail,
68 68 :filename => filename_for_content_disposition(@attachment.filename),
69 69 :type => detect_content_type(@attachment),
70 70 :disposition => 'inline'
71 71 end
72 72 else
73 73 # No thumbnail for the attachment or thumbnail could not be created
74 74 render :nothing => true, :status => 404
75 75 end
76 76 end
77 77
78 78 def upload
79 79 # Make sure that API users get used to set this content type
80 80 # as it won't trigger Rails' automatic parsing of the request body for parameters
81 81 unless request.content_type == 'application/octet-stream'
82 82 render :nothing => true, :status => 406
83 83 return
84 84 end
85 85
86 86 @attachment = Attachment.new(:file => request.raw_post)
87 87 @attachment.author = User.current
88 88 @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
89 89 @attachment.content_type = params[:content_type].presence
90 90 saved = @attachment.save
91 91
92 92 respond_to do |format|
93 93 format.js
94 94 format.api {
95 95 if saved
96 96 render :action => 'upload', :status => :created
97 97 else
98 98 render_validation_errors(@attachment)
99 99 end
100 100 }
101 101 end
102 102 end
103 103
104 104 def edit
105 105 end
106 106
107 107 def update
108 108 if params[:attachments].is_a?(Hash)
109 109 if Attachment.update_attachments(@attachments, params[:attachments])
110 110 redirect_back_or_default home_path
111 111 return
112 112 end
113 113 end
114 114 render :action => 'edit'
115 115 end
116 116
117 117 def destroy
118 118 if @attachment.container.respond_to?(:init_journal)
119 119 @attachment.container.init_journal(User.current)
120 120 end
121 121 if @attachment.container
122 122 # Make sure association callbacks are called
123 123 @attachment.container.attachments.delete(@attachment)
124 124 else
125 125 @attachment.destroy
126 126 end
127 127
128 128 respond_to do |format|
129 129 format.html { redirect_to_referer_or project_path(@project) }
130 130 format.js
131 131 end
132 132 end
133 133
134 134 private
135 135
136 136 def find_attachment
137 137 @attachment = Attachment.find(params[:id])
138 138 # Show 404 if the filename in the url is wrong
139 139 raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
140 140 @project = @attachment.project
141 141 rescue ActiveRecord::RecordNotFound
142 142 render_404
143 143 end
144 144
145 145 def find_editable_attachments
146 146 klass = params[:object_type].to_s.singularize.classify.constantize rescue nil
147 147 unless klass && klass.reflect_on_association(:attachments)
148 148 render_404
149 149 return
150 150 end
151 151
152 152 @container = klass.find(params[:object_id])
153 153 if @container.respond_to?(:visible?) && !@container.visible?
154 154 render_403
155 155 return
156 156 end
157 157 @attachments = @container.attachments.select(&:editable?)
158 158 if @container.respond_to?(:project)
159 159 @project = @container.project
160 160 end
161 161 render_404 if @attachments.empty?
162 162 rescue ActiveRecord::RecordNotFound
163 163 render_404
164 164 end
165 165
166 166 # Checks that the file exists and is readable
167 167 def file_readable
168 168 if @attachment.readable?
169 169 true
170 170 else
171 171 logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable."
172 172 render_404
173 173 end
174 174 end
175 175
176 176 def read_authorize
177 177 @attachment.visible? ? true : deny_access
178 178 end
179 179
180 180 def delete_authorize
181 181 @attachment.deletable? ? true : deny_access
182 182 end
183 183
184 184 def detect_content_type(attachment)
185 185 content_type = attachment.content_type
186 if content_type.blank?
186 if content_type.blank? || content_type == "application/octet-stream"
187 187 content_type = Redmine::MimeType.of(attachment.filename)
188 188 end
189 189 content_type.to_s
190 190 end
191 191 end
@@ -1,269 +1,269
1 1 ---
2 2 attachments_001:
3 3 created_on: 2006-07-19 21:07:27 +02:00
4 4 downloads: 0
5 5 content_type: text/plain
6 6 disk_filename: 060719210727_error281.txt
7 7 disk_directory: "2006/07"
8 8 container_id: 3
9 9 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
10 10 id: 1
11 11 container_type: Issue
12 12 filesize: 28
13 13 filename: error281.txt
14 14 author_id: 2
15 15 attachments_002:
16 16 created_on: 2007-01-27 15:08:27 +01:00
17 17 downloads: 0
18 18 content_type: text/plain
19 19 disk_filename: 060719210727_document.txt
20 20 disk_directory: "2006/07"
21 21 container_id: 1
22 22 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
23 23 id: 2
24 24 container_type: Document
25 25 filesize: 28
26 26 filename: document.txt
27 27 author_id: 2
28 28 attachments_003:
29 29 created_on: 2006-07-19 21:07:27 +02:00
30 30 downloads: 0
31 31 content_type: image/gif
32 32 disk_filename: 060719210727_logo.gif
33 33 disk_directory: "2006/07"
34 34 container_id: 4
35 35 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
36 36 id: 3
37 37 container_type: WikiPage
38 38 filesize: 280
39 39 filename: logo.gif
40 40 description: This is a logo
41 41 author_id: 2
42 42 attachments_004:
43 43 created_on: 2006-07-19 21:07:27 +02:00
44 44 container_type: Issue
45 45 container_id: 2
46 46 downloads: 0
47 47 disk_filename: 060719210727_source.rb
48 48 disk_directory: "2006/07"
49 49 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
50 50 id: 4
51 51 filesize: 153
52 52 filename: source.rb
53 53 author_id: 2
54 54 description: This is a Ruby source file
55 55 content_type: application/x-ruby
56 56 attachments_005:
57 57 created_on: 2006-07-19 21:07:27 +02:00
58 58 container_type: Issue
59 59 container_id: 3
60 60 downloads: 0
61 61 disk_filename: 060719210727_changeset_iso8859-1.diff
62 62 disk_directory: "2006/07"
63 63 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
64 64 id: 5
65 65 filesize: 687
66 66 filename: changeset_iso8859-1.diff
67 67 author_id: 2
68 68 content_type: text/x-diff
69 69 attachments_006:
70 70 created_on: 2006-07-19 21:07:27 +02:00
71 71 container_type: Issue
72 72 container_id: 3
73 73 downloads: 0
74 74 disk_filename: 060719210727_archive.zip
75 75 disk_directory: "2006/07"
76 76 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
77 77 id: 6
78 78 filesize: 157
79 79 filename: archive.zip
80 80 author_id: 2
81 content_type: application/octet-stream
81 content_type: application/zip
82 82 attachments_007:
83 83 created_on: 2006-07-19 21:07:27 +02:00
84 84 container_type: Issue
85 85 container_id: 4
86 86 downloads: 0
87 87 disk_filename: 060719210727_archive.zip
88 88 disk_directory: "2006/07"
89 89 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
90 90 id: 7
91 91 filesize: 157
92 92 filename: archive.zip
93 93 author_id: 1
94 content_type: application/octet-stream
94 content_type: application/zip
95 95 attachments_008:
96 96 created_on: 2006-07-19 21:07:27 +02:00
97 97 container_type: Project
98 98 container_id: 1
99 99 downloads: 0
100 100 disk_filename: 060719210727_project_file.zip
101 101 disk_directory: "2006/07"
102 102 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
103 103 id: 8
104 104 filesize: 320
105 105 filename: project_file.zip
106 106 author_id: 2
107 107 content_type: application/octet-stream
108 108 attachments_009:
109 109 created_on: 2006-07-19 21:07:27 +02:00
110 110 container_type: Version
111 111 container_id: 1
112 112 downloads: 0
113 113 disk_filename: 060719210727_archive.zip
114 114 disk_directory: "2006/07"
115 115 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
116 116 id: 9
117 117 filesize: 452
118 118 filename: version_file.zip
119 119 author_id: 2
120 120 content_type: application/octet-stream
121 121 attachments_010:
122 122 created_on: 2006-07-19 21:07:27 +02:00
123 123 container_type: Issue
124 124 container_id: 2
125 125 downloads: 0
126 126 disk_filename: 060719210727_picture.jpg
127 127 disk_directory: "2006/07"
128 128 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
129 129 id: 10
130 130 filesize: 452
131 131 filename: picture.jpg
132 132 author_id: 2
133 133 content_type: image/jpeg
134 134 attachments_011:
135 135 created_on: 2007-02-12 15:08:27 +01:00
136 136 container_type: Document
137 137 container_id: 1
138 138 downloads: 0
139 139 disk_filename: 060719210727_picture.jpg
140 140 disk_directory: "2006/07"
141 141 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
142 142 id: 11
143 143 filesize: 452
144 144 filename: picture.jpg
145 145 author_id: 2
146 146 content_type: image/jpeg
147 147 attachments_012:
148 148 created_on: 2006-07-19 21:07:27 +02:00
149 149 container_type: Version
150 150 container_id: 1
151 151 downloads: 0
152 152 disk_filename: 060719210727_version_file.zip
153 153 disk_directory: "2006/07"
154 154 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
155 155 id: 12
156 156 filesize: 452
157 157 filename: version_file.zip
158 158 author_id: 2
159 159 content_type: application/octet-stream
160 160 attachments_013:
161 161 created_on: 2006-07-19 21:07:27 +02:00
162 162 container_type: Message
163 163 container_id: 1
164 164 downloads: 0
165 165 disk_filename: 060719210727_foo.zip
166 166 disk_directory: "2006/07"
167 167 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
168 168 id: 13
169 169 filesize: 452
170 170 filename: foo.zip
171 171 author_id: 2
172 172 content_type: application/octet-stream
173 173 attachments_014:
174 174 created_on: 2006-07-19 21:07:27 +02:00
175 175 container_type: Issue
176 176 container_id: 3
177 177 downloads: 0
178 178 disk_filename: 060719210727_changeset_utf8.diff
179 179 disk_directory: "2006/07"
180 180 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
181 181 id: 14
182 182 filesize: 687
183 183 filename: changeset_utf8.diff
184 184 author_id: 2
185 185 content_type: text/x-diff
186 186 attachments_015:
187 187 id: 15
188 188 created_on: 2010-07-19 21:07:27 +02:00
189 189 container_type: Issue
190 190 container_id: 14
191 191 downloads: 0
192 192 disk_filename: 060719210727_changeset_utf8.diff
193 193 disk_directory: "2006/07"
194 194 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
195 195 filesize: 687
196 196 filename: private.diff
197 197 author_id: 2
198 198 content_type: text/x-diff
199 199 description: attachement of a private issue
200 200 attachments_016:
201 201 content_type: image/png
202 202 downloads: 0
203 203 created_on: 2010-11-23 16:14:50 +09:00
204 204 disk_filename: 101123161450_testfile_1.png
205 205 disk_directory: "2010/11"
206 206 container_id: 14
207 207 digest: 8e0294de2441577c529f170b6fb8f638
208 208 id: 16
209 209 container_type: Issue
210 210 description: ""
211 211 filename: testfile.png
212 212 filesize: 2654
213 213 author_id: 2
214 214 attachments_017:
215 215 content_type: image/png
216 216 downloads: 0
217 217 created_on: 2010-12-23 16:14:50 +09:00
218 218 disk_filename: 101223161450_testfile_2.png
219 219 disk_directory: "2010/12"
220 220 container_id: 14
221 221 digest: 6bc2963e8d7ea0d3e68d12d1fba3d6ca
222 222 id: 17
223 223 container_type: Issue
224 224 description: ""
225 225 filename: testfile.PNG
226 226 filesize: 3582
227 227 author_id: 2
228 228 attachments_018:
229 229 content_type: image/png
230 230 downloads: 0
231 231 created_on: 2011-01-23 16:14:50 +09:00
232 232 disk_filename: 101123161450_testfile_1.png
233 233 disk_directory: "2010/11"
234 234 container_id: 14
235 235 digest: 8e0294de2441577c529f170b6fb8f638
236 236 id: 18
237 237 container_type: Issue
238 238 description: ""
239 239 filename: testγƒ†γ‚Ήγƒˆ.png
240 240 filesize: 2654
241 241 author_id: 2
242 242 attachments_019:
243 243 content_type: image/png
244 244 downloads: 0
245 245 created_on: 2011-02-23 16:14:50 +09:00
246 246 disk_filename: 101223161450_testfile_2.png
247 247 disk_directory: "2010/12"
248 248 container_id: 14
249 249 digest: 6bc2963e8d7ea0d3e68d12d1fba3d6ca
250 250 id: 19
251 251 container_type: Issue
252 252 description: ""
253 253 filename: Testγƒ†γ‚Ήγƒˆ.PNG
254 254 filesize: 3582
255 255 author_id: 2
256 256 attachments_020:
257 257 content_type: text/plain
258 258 downloads: 0
259 259 created_on: 2012-05-12 16:14:50 +09:00
260 260 disk_filename: 120512161450_root_attachment.txt
261 261 disk_directory:
262 262 container_id: 14
263 263 digest: b0fe2abdb2599743d554a61d7da7ff74
264 264 id: 20
265 265 container_type: Issue
266 266 description: ""
267 267 filename: root_attachment.txt
268 268 filesize: 54
269 269 author_id: 2
@@ -1,458 +1,467
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2015 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../test_helper', __FILE__)
21 21
22 22 class AttachmentsControllerTest < ActionController::TestCase
23 23 fixtures :users, :projects, :roles, :members, :member_roles,
24 24 :enabled_modules, :issues, :trackers, :attachments,
25 25 :versions, :wiki_pages, :wikis, :documents
26 26
27 27 def setup
28 28 User.current = nil
29 29 set_fixtures_attachments_directory
30 30 end
31 31
32 32 def teardown
33 33 set_tmp_attachments_directory
34 34 end
35 35
36 36 def test_show_diff
37 37 ['inline', 'sbs'].each do |dt|
38 38 # 060719210727_changeset_utf8.diff
39 39 get :show, :id => 14, :type => dt
40 40 assert_response :success
41 41 assert_template 'diff'
42 42 assert_equal 'text/html', @response.content_type
43 43 assert_select 'th.filename', :text => /issues_controller.rb\t\(rΓ©vision 1484\)/
44 44 assert_select 'td.line-code', :text => /Demande créée avec succès/
45 45 end
46 46 set_tmp_attachments_directory
47 47 end
48 48
49 49 def test_show_diff_replace_cannot_convert_content
50 50 with_settings :repositories_encodings => 'UTF-8' do
51 51 ['inline', 'sbs'].each do |dt|
52 52 # 060719210727_changeset_iso8859-1.diff
53 53 get :show, :id => 5, :type => dt
54 54 assert_response :success
55 55 assert_template 'diff'
56 56 assert_equal 'text/html', @response.content_type
57 57 assert_select 'th.filename', :text => /issues_controller.rb\t\(r\?vision 1484\)/
58 58 assert_select 'td.line-code', :text => /Demande cr\?\?e avec succ\?s/
59 59 end
60 60 end
61 61 set_tmp_attachments_directory
62 62 end
63 63
64 64 def test_show_diff_latin_1
65 65 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
66 66 ['inline', 'sbs'].each do |dt|
67 67 # 060719210727_changeset_iso8859-1.diff
68 68 get :show, :id => 5, :type => dt
69 69 assert_response :success
70 70 assert_template 'diff'
71 71 assert_equal 'text/html', @response.content_type
72 72 assert_select 'th.filename', :text => /issues_controller.rb\t\(rΓ©vision 1484\)/
73 73 assert_select 'td.line-code', :text => /Demande créée avec succès/
74 74 end
75 75 end
76 76 set_tmp_attachments_directory
77 77 end
78 78
79 79 def test_save_diff_type
80 80 user1 = User.find(1)
81 81 user1.pref[:diff_type] = nil
82 82 user1.preference.save
83 83 user = User.find(1)
84 84 assert_nil user.pref[:diff_type]
85 85
86 86 @request.session[:user_id] = 1 # admin
87 87 get :show, :id => 5
88 88 assert_response :success
89 89 assert_template 'diff'
90 90 user.reload
91 91 assert_equal "inline", user.pref[:diff_type]
92 92 get :show, :id => 5, :type => 'sbs'
93 93 assert_response :success
94 94 assert_template 'diff'
95 95 user.reload
96 96 assert_equal "sbs", user.pref[:diff_type]
97 97 end
98 98
99 99 def test_diff_show_filename_in_mercurial_export
100 100 set_tmp_attachments_directory
101 101 a = Attachment.new(:container => Issue.find(1),
102 102 :file => uploaded_test_file("hg-export.diff", "text/plain"),
103 103 :author => User.find(1))
104 104 assert a.save
105 105 assert_equal 'hg-export.diff', a.filename
106 106
107 107 get :show, :id => a.id, :type => 'inline'
108 108 assert_response :success
109 109 assert_template 'diff'
110 110 assert_equal 'text/html', @response.content_type
111 111 assert_select 'th.filename', :text => 'test1.txt'
112 112 end
113 113
114 114 def test_show_text_file
115 115 get :show, :id => 4
116 116 assert_response :success
117 117 assert_template 'file'
118 118 assert_equal 'text/html', @response.content_type
119 119 set_tmp_attachments_directory
120 120 end
121 121
122 122 def test_show_text_file_utf_8
123 123 set_tmp_attachments_directory
124 124 a = Attachment.new(:container => Issue.find(1),
125 125 :file => uploaded_test_file("japanese-utf-8.txt", "text/plain"),
126 126 :author => User.find(1))
127 127 assert a.save
128 128 assert_equal 'japanese-utf-8.txt', a.filename
129 129
130 130 str_japanese = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
131 131
132 132 get :show, :id => a.id
133 133 assert_response :success
134 134 assert_template 'file'
135 135 assert_equal 'text/html', @response.content_type
136 136 assert_select 'tr#L1' do
137 137 assert_select 'th.line-num', :text => '1'
138 138 assert_select 'td', :text => /#{str_japanese}/
139 139 end
140 140 end
141 141
142 142 def test_show_text_file_replace_cannot_convert_content
143 143 set_tmp_attachments_directory
144 144 with_settings :repositories_encodings => 'UTF-8' do
145 145 a = Attachment.new(:container => Issue.find(1),
146 146 :file => uploaded_test_file("iso8859-1.txt", "text/plain"),
147 147 :author => User.find(1))
148 148 assert a.save
149 149 assert_equal 'iso8859-1.txt', a.filename
150 150
151 151 get :show, :id => a.id
152 152 assert_response :success
153 153 assert_template 'file'
154 154 assert_equal 'text/html', @response.content_type
155 155 assert_select 'tr#L7' do
156 156 assert_select 'th.line-num', :text => '7'
157 157 assert_select 'td', :text => /Demande cr\?\?e avec succ\?s/
158 158 end
159 159 end
160 160 end
161 161
162 162 def test_show_text_file_latin_1
163 163 set_tmp_attachments_directory
164 164 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
165 165 a = Attachment.new(:container => Issue.find(1),
166 166 :file => uploaded_test_file("iso8859-1.txt", "text/plain"),
167 167 :author => User.find(1))
168 168 assert a.save
169 169 assert_equal 'iso8859-1.txt', a.filename
170 170
171 171 get :show, :id => a.id
172 172 assert_response :success
173 173 assert_template 'file'
174 174 assert_equal 'text/html', @response.content_type
175 175 assert_select 'tr#L7' do
176 176 assert_select 'th.line-num', :text => '7'
177 177 assert_select 'td', :text => /Demande créée avec succès/
178 178 end
179 179 end
180 180 end
181 181
182 182 def test_show_text_file_should_send_if_too_big
183 183 with_settings :file_max_size_displayed => 512 do
184 184 Attachment.find(4).update_attribute :filesize, 754.kilobyte
185 185 get :show, :id => 4
186 186 assert_response :success
187 187 assert_equal 'application/x-ruby', @response.content_type
188 188 end
189 189 set_tmp_attachments_directory
190 190 end
191 191
192 192 def test_show_other
193 193 get :show, :id => 6
194 194 assert_response :success
195 assert_equal 'application/octet-stream', @response.content_type
195 assert_equal 'application/zip', @response.content_type
196 196 set_tmp_attachments_directory
197 197 end
198 198
199 199 def test_show_file_from_private_issue_without_permission
200 200 get :show, :id => 15
201 201 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2F15'
202 202 set_tmp_attachments_directory
203 203 end
204 204
205 205 def test_show_file_from_private_issue_with_permission
206 206 @request.session[:user_id] = 2
207 207 get :show, :id => 15
208 208 assert_response :success
209 209 assert_select 'h2', :text => /private.diff/
210 210 set_tmp_attachments_directory
211 211 end
212 212
213 213 def test_show_file_without_container_should_be_allowed_to_author
214 214 set_tmp_attachments_directory
215 215 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
216 216
217 217 @request.session[:user_id] = 2
218 218 get :show, :id => attachment.id
219 219 assert_response 200
220 220 end
221 221
222 222 def test_show_file_without_container_should_be_denied_to_other_users
223 223 set_tmp_attachments_directory
224 224 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
225 225
226 226 @request.session[:user_id] = 3
227 227 get :show, :id => attachment.id
228 228 assert_response 403
229 229 end
230 230
231 231 def test_show_invalid_should_respond_with_404
232 232 get :show, :id => 999
233 233 assert_response 404
234 234 end
235 235
236 236 def test_download_text_file
237 237 get :download, :id => 4
238 238 assert_response :success
239 239 assert_equal 'application/x-ruby', @response.content_type
240 240 etag = @response.etag
241 241 assert_not_nil etag
242 242
243 243 @request.env["HTTP_IF_NONE_MATCH"] = etag
244 244 get :download, :id => 4
245 245 assert_response 304
246 246
247 247 set_tmp_attachments_directory
248 248 end
249 249
250 250 def test_download_version_file_with_issue_tracking_disabled
251 251 Project.find(1).disable_module! :issue_tracking
252 252 get :download, :id => 9
253 253 assert_response :success
254 254 end
255 255
256 256 def test_download_should_assign_content_type_if_blank
257 257 Attachment.find(4).update_attribute(:content_type, '')
258 258
259 259 get :download, :id => 4
260 260 assert_response :success
261 261 assert_equal 'text/x-ruby', @response.content_type
262 262 set_tmp_attachments_directory
263 263 end
264 264
265 def test_download_should_assign_better_content_type_than_application_octet_stream
266 Attachment.find(4).update! :content_type => "application/octet-stream"
267
268 get :download, :id => 4
269 assert_response :success
270 assert_equal 'text/x-ruby', @response.content_type
271 set_tmp_attachments_directory
272 end
273
265 274 def test_download_missing_file
266 275 get :download, :id => 2
267 276 assert_response 404
268 277 set_tmp_attachments_directory
269 278 end
270 279
271 280 def test_download_should_be_denied_without_permission
272 281 get :download, :id => 7
273 282 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fdownload%2F7'
274 283 set_tmp_attachments_directory
275 284 end
276 285
277 286 if convert_installed?
278 287 def test_thumbnail
279 288 Attachment.clear_thumbnails
280 289 @request.session[:user_id] = 2
281 290 get :thumbnail, :id => 16
282 291 assert_response :success
283 292 assert_equal 'image/png', response.content_type
284 293
285 294 etag = @response.etag
286 295 assert_not_nil etag
287 296
288 297 @request.env["HTTP_IF_NONE_MATCH"] = etag
289 298 get :thumbnail, :id => 16
290 299 assert_response 304
291 300 end
292 301
293 302 def test_thumbnail_should_not_exceed_maximum_size
294 303 Redmine::Thumbnail.expects(:generate).with {|source, target, size| size == 800}
295 304
296 305 @request.session[:user_id] = 2
297 306 get :thumbnail, :id => 16, :size => 2000
298 307 end
299 308
300 309 def test_thumbnail_should_round_size
301 310 Redmine::Thumbnail.expects(:generate).with {|source, target, size| size == 250}
302 311
303 312 @request.session[:user_id] = 2
304 313 get :thumbnail, :id => 16, :size => 260
305 314 end
306 315
307 316 def test_thumbnail_should_return_404_for_non_image_attachment
308 317 @request.session[:user_id] = 2
309 318
310 319 get :thumbnail, :id => 15
311 320 assert_response 404
312 321 end
313 322
314 323 def test_thumbnail_should_return_404_if_thumbnail_generation_failed
315 324 Attachment.any_instance.stubs(:thumbnail).returns(nil)
316 325 @request.session[:user_id] = 2
317 326
318 327 get :thumbnail, :id => 16
319 328 assert_response 404
320 329 end
321 330
322 331 def test_thumbnail_should_be_denied_without_permission
323 332 get :thumbnail, :id => 16
324 333 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fthumbnail%2F16'
325 334 end
326 335 else
327 336 puts '(ImageMagick convert not available)'
328 337 end
329 338
330 339 def test_edit
331 340 @request.session[:user_id] = 2
332 341 get :edit, :object_type => 'issues', :object_id => '2'
333 342 assert_response :success
334 343 assert_template 'edit'
335 344
336 345 container = Issue.find(2)
337 346 assert_equal container, assigns(:container)
338 347 assert_equal container.attachments.size, assigns(:attachments).size
339 348
340 349 assert_select 'form[action=?]', '/attachments/issues/2' do
341 350 assert_select 'tr#attachment-4' do
342 351 assert_select 'input[name=?][value=?]', 'attachments[4][filename]', 'source.rb'
343 352 assert_select 'input[name=?][value=?]', 'attachments[4][description]', 'This is a Ruby source file'
344 353 end
345 354 end
346 355 end
347 356
348 357 def test_edit_invalid_container_class_should_return_404
349 358 get :edit, :object_type => 'nuggets', :object_id => '3'
350 359 assert_response 404
351 360 end
352 361
353 362 def test_edit_invalid_object_should_return_404
354 363 get :edit, :object_type => 'issues', :object_id => '999'
355 364 assert_response 404
356 365 end
357 366
358 367 def test_edit_for_object_that_is_not_visible_should_return_403
359 368 get :edit, :object_type => 'issues', :object_id => '4'
360 369 assert_response 403
361 370 end
362 371
363 372 def test_update
364 373 @request.session[:user_id] = 2
365 374 patch :update, :object_type => 'issues', :object_id => '2', :attachments => {
366 375 '1' => {:filename => 'newname.text', :description => ''},
367 376 '4' => {:filename => 'newname.rb', :description => 'Renamed'},
368 377 }
369 378
370 379 assert_response 302
371 380 attachment = Attachment.find(4)
372 381 assert_equal 'newname.rb', attachment.filename
373 382 assert_equal 'Renamed', attachment.description
374 383 end
375 384
376 385 def test_update_with_failure
377 386 @request.session[:user_id] = 2
378 387 patch :update, :object_type => 'issues', :object_id => '3', :attachments => {
379 388 '1' => {:filename => '', :description => ''},
380 389 '4' => {:filename => 'newname.rb', :description => 'Renamed'},
381 390 }
382 391
383 392 assert_response :success
384 393 assert_template 'edit'
385 394 assert_select_error /file cannot be blank/i
386 395
387 396 # The other attachment should not be updated
388 397 attachment = Attachment.find(4)
389 398 assert_equal 'source.rb', attachment.filename
390 399 assert_equal 'This is a Ruby source file', attachment.description
391 400 end
392 401
393 402 def test_destroy_issue_attachment
394 403 set_tmp_attachments_directory
395 404 issue = Issue.find(3)
396 405 @request.session[:user_id] = 2
397 406
398 407 assert_difference 'issue.attachments.count', -1 do
399 408 assert_difference 'Journal.count' do
400 409 delete :destroy, :id => 1
401 410 assert_redirected_to '/projects/ecookbook'
402 411 end
403 412 end
404 413 assert_nil Attachment.find_by_id(1)
405 414 j = Journal.order('id DESC').first
406 415 assert_equal issue, j.journalized
407 416 assert_equal 'attachment', j.details.first.property
408 417 assert_equal '1', j.details.first.prop_key
409 418 assert_equal 'error281.txt', j.details.first.old_value
410 419 assert_equal User.find(2), j.user
411 420 end
412 421
413 422 def test_destroy_wiki_page_attachment
414 423 set_tmp_attachments_directory
415 424 @request.session[:user_id] = 2
416 425 assert_difference 'Attachment.count', -1 do
417 426 delete :destroy, :id => 3
418 427 assert_response 302
419 428 end
420 429 end
421 430
422 431 def test_destroy_project_attachment
423 432 set_tmp_attachments_directory
424 433 @request.session[:user_id] = 2
425 434 assert_difference 'Attachment.count', -1 do
426 435 delete :destroy, :id => 8
427 436 assert_response 302
428 437 end
429 438 end
430 439
431 440 def test_destroy_version_attachment
432 441 set_tmp_attachments_directory
433 442 @request.session[:user_id] = 2
434 443 assert_difference 'Attachment.count', -1 do
435 444 delete :destroy, :id => 9
436 445 assert_response 302
437 446 end
438 447 end
439 448
440 449 def test_destroy_version_attachment_with_issue_tracking_disabled
441 450 Project.find(1).disable_module! :issue_tracking
442 451 set_tmp_attachments_directory
443 452 @request.session[:user_id] = 2
444 453 assert_difference 'Attachment.count', -1 do
445 454 delete :destroy, :id => 9
446 455 assert_response 302
447 456 end
448 457 end
449 458
450 459 def test_destroy_without_permission
451 460 set_tmp_attachments_directory
452 461 assert_no_difference 'Attachment.count' do
453 462 delete :destroy, :id => 3
454 463 end
455 464 assert_response 302
456 465 assert Attachment.find_by_id(3)
457 466 end
458 467 end
@@ -1,156 +1,156
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::AttachmentsTest < Redmine::ApiTest::Base
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :enumerations, :users, :issue_categories,
23 23 :projects_trackers,
24 24 :roles,
25 25 :member_roles,
26 26 :members,
27 27 :enabled_modules,
28 28 :attachments
29 29
30 30 def setup
31 31 super
32 32 set_fixtures_attachments_directory
33 33 end
34 34
35 35 def teardown
36 36 super
37 37 set_tmp_attachments_directory
38 38 end
39 39
40 40 test "GET /attachments/:id.xml should return the attachment" do
41 41 get '/attachments/7.xml', {}, credentials('jsmith')
42 42 assert_response :success
43 43 assert_equal 'application/xml', @response.content_type
44 44 assert_select 'attachment id', :text => '7' do
45 45 assert_select '~ filename', :text => 'archive.zip'
46 46 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/7/archive.zip'
47 47 end
48 48 end
49 49
50 50 test "GET /attachments/:id.xml for image should include thumbnail_url" do
51 51 get '/attachments/16.xml', {}, credentials('jsmith')
52 52 assert_response :success
53 53 assert_equal 'application/xml', @response.content_type
54 54 assert_select 'attachment id:contains(16)' do
55 55 assert_select '~ thumbnail_url', :text => 'http://www.example.com/attachments/thumbnail/16'
56 56 end
57 57 end
58 58
59 59 test "GET /attachments/:id.xml should deny access without credentials" do
60 60 get '/attachments/7.xml'
61 61 assert_response 401
62 62 set_tmp_attachments_directory
63 63 end
64 64
65 65 test "GET /attachments/download/:id/:filename should return the attachment content" do
66 66 get '/attachments/download/7/archive.zip', {}, credentials('jsmith')
67 67 assert_response :success
68 assert_equal 'application/octet-stream', @response.content_type
68 assert_equal 'application/zip', @response.content_type
69 69 set_tmp_attachments_directory
70 70 end
71 71
72 72 test "GET /attachments/download/:id/:filename should deny access without credentials" do
73 73 get '/attachments/download/7/archive.zip'
74 74 assert_response 302
75 75 set_tmp_attachments_directory
76 76 end
77 77
78 78 test "GET /attachments/thumbnail/:id should return the thumbnail" do
79 79 skip unless convert_installed?
80 80 get '/attachments/thumbnail/16', {}, credentials('jsmith')
81 81 assert_response :success
82 82 end
83 83
84 84 test "POST /uploads.xml should return the token" do
85 85 set_tmp_attachments_directory
86 86 assert_difference 'Attachment.count' do
87 87 post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
88 88 assert_response :created
89 89 assert_equal 'application/xml', response.content_type
90 90 end
91 91
92 92 xml = Hash.from_xml(response.body)
93 93 assert_kind_of Hash, xml['upload']
94 94 token = xml['upload']['token']
95 95 assert_not_nil token
96 96
97 97 attachment = Attachment.order('id DESC').first
98 98 assert_equal token, attachment.token
99 99 assert_nil attachment.container
100 100 assert_equal 2, attachment.author_id
101 101 assert_equal 'File content'.size, attachment.filesize
102 102 assert attachment.content_type.blank?
103 103 assert attachment.filename.present?
104 104 assert_match /\d+_[0-9a-z]+/, attachment.diskfile
105 105 assert File.exist?(attachment.diskfile)
106 106 assert_equal 'File content', File.read(attachment.diskfile)
107 107 end
108 108
109 109 test "POST /uploads.json should return the token" do
110 110 set_tmp_attachments_directory
111 111 assert_difference 'Attachment.count' do
112 112 post '/uploads.json', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
113 113 assert_response :created
114 114 assert_equal 'application/json', response.content_type
115 115 end
116 116
117 117 json = ActiveSupport::JSON.decode(response.body)
118 118 assert_kind_of Hash, json['upload']
119 119 token = json['upload']['token']
120 120 assert_not_nil token
121 121
122 122 attachment = Attachment.order('id DESC').first
123 123 assert_equal token, attachment.token
124 124 end
125 125
126 126 test "POST /uploads.xml should accept :filename param as the attachment filename" do
127 127 set_tmp_attachments_directory
128 128 assert_difference 'Attachment.count' do
129 129 post '/uploads.xml?filename=test.txt', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
130 130 assert_response :created
131 131 end
132 132
133 133 attachment = Attachment.order('id DESC').first
134 134 assert_equal 'test.txt', attachment.filename
135 135 assert_match /_test\.txt$/, attachment.diskfile
136 136 end
137 137
138 138 test "POST /uploads.xml should not accept other content types" do
139 139 set_tmp_attachments_directory
140 140 assert_no_difference 'Attachment.count' do
141 141 post '/uploads.xml', 'PNG DATA', {"CONTENT_TYPE" => 'image/png'}.merge(credentials('jsmith'))
142 142 assert_response 406
143 143 end
144 144 end
145 145
146 146 test "POST /uploads.xml should return errors if file is too big" do
147 147 set_tmp_attachments_directory
148 148 with_settings :attachment_max_size => 1 do
149 149 assert_no_difference 'Attachment.count' do
150 150 post '/uploads.xml', ('x' * 2048), {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
151 151 assert_response 422
152 152 assert_select 'error', :text => /exceeds the maximum allowed file size/
153 153 end
154 154 end
155 155 end
156 156 end
General Comments 0
You need to be logged in to leave comments. Login now