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