##// END OF EJS Templates
File viewer for attached text files....
Jean-Philippe Lang -
r1506:80a7486f95a3
parent child
Show More
@@ -0,0 +1,15
1 <h2><%=h @attachment.filename %></h2>
2
3 <div class="attachments">
4 <p><%= h("#{@attachment.description} - ") unless @attachment.description.blank? %>
5 <span class="author"><%= @attachment.author %>, <%= format_time(@attachment.created_on) %></span></p>
6 <p><%= link_to l(:button_download), {:controller => 'attachments', :action => 'download', :id => @attachment } -%>
7 <span class="size">(<%= number_to_human_size @attachment.filesize %>)</span></p>
8
9 </div>
10 &nbsp;
11 <%= render :partial => 'common/file', :locals => {:content => @content, :filename => @attachment.filename} %>
12
13 <% content_for :header_tags do -%>
14 <%= stylesheet_link_tag "scm" -%>
15 <% end -%>
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,13
1 Index: trunk/app/controllers/issues_controller.rb
2 ===================================================================
3 --- trunk/app/controllers/issues_controller.rb (rοΏ½vision 1483)
4 +++ trunk/app/controllers/issues_controller.rb (rοΏ½vision 1484)
5 @@ -149,7 +149,7 @@
6 attach_files(@issue, params[:attachments])
7 flash[:notice] = l(:notice_successful_create)
8 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
9 - redirect_to :controller => 'issues', :action => 'show', :id => @issue, :project_id => @project
10 + redirect_to :controller => 'issues', :action => 'show', :id => @issue
11 return
12 end
13 end
@@ -0,0 +1,10
1 # The Greeter class
2 class Greeter
3 def initialize(name)
4 @name = name.capitalize
5 end
6
7 def salute
8 puts "Hello #{@name}!"
9 end
10 end
@@ -0,0 +1,59
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'attachments_controller'
20
21 # Re-raise errors caught by the controller.
22 class AttachmentsController; def rescue_action(e) raise e end; end
23
24
25 class AttachmentsControllerTest < Test::Unit::TestCase
26 fixtures :users, :projects, :issues, :attachments
27
28 def setup
29 @controller = AttachmentsController.new
30 @request = ActionController::TestRequest.new
31 @response = ActionController::TestResponse.new
32 Attachment.storage_path = "#{RAILS_ROOT}/test/fixtures/files"
33 User.current = nil
34 end
35
36 def test_show_diff
37 get :show, :id => 5
38 assert_response :success
39 assert_template 'diff'
40 end
41
42 def test_show_text_file
43 get :show, :id => 4
44 assert_response :success
45 assert_template 'file'
46 end
47
48 def test_show_other
49 get :show, :id => 6
50 assert_response :success
51 assert_equal 'application/octet-stream', @response.content_type
52 end
53
54 def test_download_text_file
55 get :download, :id => 4
56 assert_response :success
57 assert_equal 'application/x-ruby', @response.content_type
58 end
59 end
@@ -1,46 +1,49
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 layout 'base'
20 20 before_filter :find_project, :check_project_privacy
21 21
22 22 def show
23 23 if @attachment.is_diff?
24 24 @diff = File.new(@attachment.diskfile, "rb").read
25 25 render :action => 'diff'
26 else
26 elsif @attachment.is_text?
27 @content = File.new(@attachment.diskfile, "rb").read
28 render :action => 'file'
29 elsif
27 30 download
28 31 end
29 32 end
30 33
31 34 def download
32 35 # images are sent inline
33 36 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
34 37 :type => @attachment.content_type,
35 38 :disposition => (@attachment.image? ? 'inline' : 'attachment')
36 39 end
37 40
38 41 private
39 42 def find_project
40 43 @attachment = Attachment.find(params[:id])
41 render_404 and return false unless File.readable?(@attachment.diskfile)
44 #render_404 and return false unless File.readable?(@attachment.diskfile)
42 45 @project = @attachment.project
43 rescue
44 render_404
46 #rescue
47 # render_404
45 48 end
46 49 end
@@ -1,517 +1,525
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 require 'coderay'
19 require 'coderay/helpers/file_type'
20
18 21 module ApplicationHelper
19 22 include Redmine::WikiFormatting::Macros::Definitions
20 23
21 24 def current_role
22 25 @current_role ||= User.current.role_for_project(@project)
23 26 end
24 27
25 28 # Return true if user is authorized for controller/action, otherwise false
26 29 def authorize_for(controller, action)
27 30 User.current.allowed_to?({:controller => controller, :action => action}, @project)
28 31 end
29 32
30 33 # Display a link if user is authorized
31 34 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
32 35 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
33 36 end
34 37
35 38 # Display a link to user's account page
36 39 def link_to_user(user)
37 40 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
38 41 end
39 42
40 43 def link_to_issue(issue, options={})
41 44 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
42 45 end
43 46
44 47 def toggle_link(name, id, options={})
45 48 onclick = "Element.toggle('#{id}'); "
46 49 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
47 50 onclick << "return false;"
48 51 link_to(name, "#", :onclick => onclick)
49 52 end
50 53
51 54 def show_and_goto_link(name, id, options={})
52 55 onclick = "Element.show('#{id}'); "
53 56 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
54 57 onclick << "Element.scrollTo('#{id}'); "
55 58 onclick << "return false;"
56 59 link_to(name, "#", options.merge(:onclick => onclick))
57 60 end
58 61
59 62 def image_to_function(name, function, html_options = {})
60 63 html_options.symbolize_keys!
61 64 tag(:input, html_options.merge({
62 65 :type => "image", :src => image_path(name),
63 66 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
64 67 }))
65 68 end
66 69
67 70 def prompt_to_remote(name, text, param, url, html_options = {})
68 71 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
69 72 link_to name, {}, html_options
70 73 end
71 74
72 75 def format_date(date)
73 76 return nil unless date
74 77 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
75 78 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
76 79 date.strftime(@date_format)
77 80 end
78 81
79 82 def format_time(time, include_date = true)
80 83 return nil unless time
81 84 time = time.to_time if time.is_a?(String)
82 85 zone = User.current.time_zone
83 86 if time.utc?
84 87 local = zone ? zone.adjust(time) : time.getlocal
85 88 else
86 89 local = zone ? zone.adjust(time.getutc) : time
87 90 end
88 91 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
89 92 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
90 93 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
91 94 end
92 95
93 96 # Truncates and returns the string as a single line
94 97 def truncate_single_line(string, *args)
95 98 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
96 99 end
97 100
98 101 def html_hours(text)
99 102 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
100 103 end
101 104
102 105 def authoring(created, author)
103 106 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
104 107 l(:label_added_time_by, author || 'Anonymous', time_tag)
105 108 end
106 109
107 110 def l_or_humanize(s)
108 111 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
109 112 end
110 113
111 114 def day_name(day)
112 115 l(:general_day_names).split(',')[day-1]
113 116 end
114 117
115 118 def month_name(month)
116 119 l(:actionview_datehelper_select_month_names).split(',')[month-1]
117 120 end
118 121
122 def syntax_highlight(name, content)
123 type = CodeRay::FileType[name]
124 type ? CodeRay.scan(content, type).html : h(content)
125 end
126
119 127 def pagination_links_full(paginator, count=nil, options={})
120 128 page_param = options.delete(:page_param) || :page
121 129 url_param = params.dup
122 130 # don't reuse params if filters are present
123 131 url_param.clear if url_param.has_key?(:set_filter)
124 132
125 133 html = ''
126 134 html << link_to_remote(('&#171; ' + l(:label_previous)),
127 135 {:update => 'content',
128 136 :url => url_param.merge(page_param => paginator.current.previous),
129 137 :complete => 'window.scrollTo(0,0)'},
130 138 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
131 139
132 140 html << (pagination_links_each(paginator, options) do |n|
133 141 link_to_remote(n.to_s,
134 142 {:url => {:params => url_param.merge(page_param => n)},
135 143 :update => 'content',
136 144 :complete => 'window.scrollTo(0,0)'},
137 145 {:href => url_for(:params => url_param.merge(page_param => n))})
138 146 end || '')
139 147
140 148 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
141 149 {:update => 'content',
142 150 :url => url_param.merge(page_param => paginator.current.next),
143 151 :complete => 'window.scrollTo(0,0)'},
144 152 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
145 153
146 154 unless count.nil?
147 155 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
148 156 end
149 157
150 158 html
151 159 end
152 160
153 161 def per_page_links(selected=nil)
154 162 url_param = params.dup
155 163 url_param.clear if url_param.has_key?(:set_filter)
156 164
157 165 links = Setting.per_page_options_array.collect do |n|
158 166 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
159 167 {:href => url_for(url_param.merge(:per_page => n))})
160 168 end
161 169 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
162 170 end
163 171
164 172 def breadcrumb(*args)
165 173 content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb')
166 174 end
167 175
168 176 def html_title(*args)
169 177 if args.empty?
170 178 title = []
171 179 title << @project.name if @project
172 180 title += @html_title if @html_title
173 181 title << Setting.app_title
174 182 title.compact.join(' - ')
175 183 else
176 184 @html_title ||= []
177 185 @html_title += args
178 186 end
179 187 end
180 188
181 189 def accesskey(s)
182 190 Redmine::AccessKeys.key_for s
183 191 end
184 192
185 193 # Formats text according to system settings.
186 194 # 2 ways to call this method:
187 195 # * with a String: textilizable(text, options)
188 196 # * with an object and one of its attribute: textilizable(issue, :description, options)
189 197 def textilizable(*args)
190 198 options = args.last.is_a?(Hash) ? args.pop : {}
191 199 case args.size
192 200 when 1
193 201 obj = nil
194 202 text = args.shift
195 203 when 2
196 204 obj = args.shift
197 205 text = obj.send(args.shift).to_s
198 206 else
199 207 raise ArgumentError, 'invalid arguments to textilizable'
200 208 end
201 209 return '' if text.blank?
202 210
203 211 only_path = options.delete(:only_path) == false ? false : true
204 212
205 213 # when using an image link, try to use an attachment, if possible
206 214 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
207 215
208 216 if attachments
209 217 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
210 218 style = $1
211 219 filename = $6
212 220 rf = Regexp.new(filename, Regexp::IGNORECASE)
213 221 # search for the picture in attachments
214 222 if found = attachments.detect { |att| att.filename =~ rf }
215 223 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
216 224 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
217 225 alt = desc.blank? ? nil : "(#{desc})"
218 226 "!#{style}#{image_url}#{alt}!"
219 227 else
220 228 "!#{style}#{filename}!"
221 229 end
222 230 end
223 231 end
224 232
225 233 text = (Setting.text_formatting == 'textile') ?
226 234 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
227 235 simple_format(auto_link(h(text)))
228 236
229 237 # different methods for formatting wiki links
230 238 case options[:wiki_links]
231 239 when :local
232 240 # used for local links to html files
233 241 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
234 242 when :anchor
235 243 # used for single-file wiki export
236 244 format_wiki_link = Proc.new {|project, title| "##{title}" }
237 245 else
238 246 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
239 247 end
240 248
241 249 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
242 250
243 251 # Wiki links
244 252 #
245 253 # Examples:
246 254 # [[mypage]]
247 255 # [[mypage|mytext]]
248 256 # wiki links can refer other project wikis, using project name or identifier:
249 257 # [[project:]] -> wiki starting page
250 258 # [[project:|mytext]]
251 259 # [[project:mypage]]
252 260 # [[project:mypage|mytext]]
253 261 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
254 262 link_project = project
255 263 esc, all, page, title = $1, $2, $3, $5
256 264 if esc.nil?
257 265 if page =~ /^([^\:]+)\:(.*)$/
258 266 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
259 267 page = $2
260 268 title ||= $1 if page.blank?
261 269 end
262 270
263 271 if link_project && link_project.wiki
264 272 # check if page exists
265 273 wiki_page = link_project.wiki.find_page(page)
266 274 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
267 275 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
268 276 else
269 277 # project or wiki doesn't exist
270 278 title || page
271 279 end
272 280 else
273 281 all
274 282 end
275 283 end
276 284
277 285 # Redmine links
278 286 #
279 287 # Examples:
280 288 # Issues:
281 289 # #52 -> Link to issue #52
282 290 # Changesets:
283 291 # r52 -> Link to revision 52
284 292 # commit:a85130f -> Link to scmid starting with a85130f
285 293 # Documents:
286 294 # document#17 -> Link to document with id 17
287 295 # document:Greetings -> Link to the document with title "Greetings"
288 296 # document:"Some document" -> Link to the document with title "Some document"
289 297 # Versions:
290 298 # version#3 -> Link to version with id 3
291 299 # version:1.0.0 -> Link to version named "1.0.0"
292 300 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
293 301 # Attachments:
294 302 # attachment:file.zip -> Link to the attachment of the current object named file.zip
295 303 # Source files:
296 304 # source:some/file -> Link to the file located at /some/file in the project's repository
297 305 # source:some/file@52 -> Link to the file's revision 52
298 306 # source:some/file#L120 -> Link to line 120 of the file
299 307 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
300 308 # export:some/file -> Force the download of the file
301 309 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
302 310 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
303 311 link = nil
304 312 if esc.nil?
305 313 if prefix.nil? && sep == 'r'
306 314 if project && (changeset = project.changesets.find_by_revision(oid))
307 315 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
308 316 :class => 'changeset',
309 317 :title => truncate_single_line(changeset.comments, 100))
310 318 end
311 319 elsif sep == '#'
312 320 oid = oid.to_i
313 321 case prefix
314 322 when nil
315 323 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
316 324 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
317 325 :class => (issue.closed? ? 'issue closed' : 'issue'),
318 326 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
319 327 link = content_tag('del', link) if issue.closed?
320 328 end
321 329 when 'document'
322 330 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
323 331 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
324 332 :class => 'document'
325 333 end
326 334 when 'version'
327 335 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
328 336 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
329 337 :class => 'version'
330 338 end
331 339 end
332 340 elsif sep == ':'
333 341 # removes the double quotes if any
334 342 name = oid.gsub(%r{^"(.*)"$}, "\\1")
335 343 case prefix
336 344 when 'document'
337 345 if project && document = project.documents.find_by_title(name)
338 346 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
339 347 :class => 'document'
340 348 end
341 349 when 'version'
342 350 if project && version = project.versions.find_by_name(name)
343 351 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
344 352 :class => 'version'
345 353 end
346 354 when 'commit'
347 355 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
348 356 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
349 357 :class => 'changeset',
350 358 :title => truncate_single_line(changeset.comments, 100)
351 359 end
352 360 when 'source', 'export'
353 361 if project && project.repository
354 362 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
355 363 path, rev, anchor = $1, $3, $5
356 364 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :path => path,
357 365 :rev => rev,
358 366 :anchor => anchor,
359 367 :format => (prefix == 'export' ? 'raw' : nil)},
360 368 :class => (prefix == 'export' ? 'source download' : 'source')
361 369 end
362 370 when 'attachment'
363 371 if attachments && attachment = attachments.detect {|a| a.filename == name }
364 372 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
365 373 :class => 'attachment'
366 374 end
367 375 end
368 376 end
369 377 end
370 378 leading + (link || "#{prefix}#{sep}#{oid}")
371 379 end
372 380
373 381 text
374 382 end
375 383
376 384 # Same as Rails' simple_format helper without using paragraphs
377 385 def simple_format_without_paragraph(text)
378 386 text.to_s.
379 387 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
380 388 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
381 389 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
382 390 end
383 391
384 392 def error_messages_for(object_name, options = {})
385 393 options = options.symbolize_keys
386 394 object = instance_variable_get("@#{object_name}")
387 395 if object && !object.errors.empty?
388 396 # build full_messages here with controller current language
389 397 full_messages = []
390 398 object.errors.each do |attr, msg|
391 399 next if msg.nil?
392 400 msg = msg.first if msg.is_a? Array
393 401 if attr == "base"
394 402 full_messages << l(msg)
395 403 else
396 404 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
397 405 end
398 406 end
399 407 # retrieve custom values error messages
400 408 if object.errors[:custom_values]
401 409 object.custom_values.each do |v|
402 410 v.errors.each do |attr, msg|
403 411 next if msg.nil?
404 412 msg = msg.first if msg.is_a? Array
405 413 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
406 414 end
407 415 end
408 416 end
409 417 content_tag("div",
410 418 content_tag(
411 419 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
412 420 ) +
413 421 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
414 422 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
415 423 )
416 424 else
417 425 ""
418 426 end
419 427 end
420 428
421 429 def lang_options_for_select(blank=true)
422 430 (blank ? [["(auto)", ""]] : []) +
423 431 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
424 432 end
425 433
426 434 def label_tag_for(name, option_tags = nil, options = {})
427 435 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
428 436 content_tag("label", label_text)
429 437 end
430 438
431 439 def labelled_tabular_form_for(name, object, options, &proc)
432 440 options[:html] ||= {}
433 441 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
434 442 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
435 443 end
436 444
437 445 def back_url_hidden_field_tag
438 446 hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
439 447 end
440 448
441 449 def check_all_links(form_name)
442 450 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
443 451 " | " +
444 452 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
445 453 end
446 454
447 455 def progress_bar(pcts, options={})
448 456 pcts = [pcts, pcts] unless pcts.is_a?(Array)
449 457 pcts[1] = pcts[1] - pcts[0]
450 458 pcts << (100 - pcts[1] - pcts[0])
451 459 width = options[:width] || '100px;'
452 460 legend = options[:legend] || ''
453 461 content_tag('table',
454 462 content_tag('tr',
455 463 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
456 464 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
457 465 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
458 466 ), :class => 'progress', :style => "width: #{width};") +
459 467 content_tag('p', legend, :class => 'pourcent')
460 468 end
461 469
462 470 def context_menu_link(name, url, options={})
463 471 options[:class] ||= ''
464 472 if options.delete(:selected)
465 473 options[:class] << ' icon-checked disabled'
466 474 options[:disabled] = true
467 475 end
468 476 if options.delete(:disabled)
469 477 options.delete(:method)
470 478 options.delete(:confirm)
471 479 options.delete(:onclick)
472 480 options[:class] << ' disabled'
473 481 url = '#'
474 482 end
475 483 link_to name, url, options
476 484 end
477 485
478 486 def calendar_for(field_id)
479 487 include_calendar_headers_tags
480 488 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
481 489 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
482 490 end
483 491
484 492 def include_calendar_headers_tags
485 493 unless @calendar_headers_tags_included
486 494 @calendar_headers_tags_included = true
487 495 content_for :header_tags do
488 496 javascript_include_tag('calendar/calendar') +
489 497 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
490 498 javascript_include_tag('calendar/calendar-setup') +
491 499 stylesheet_link_tag('calendar')
492 500 end
493 501 end
494 502 end
495 503
496 504 def wikitoolbar_for(field_id)
497 505 return '' unless Setting.text_formatting == 'textile'
498 506
499 507 help_link = l(:setting_text_formatting) + ': ' +
500 508 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
501 509 :onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
502 510
503 511 javascript_include_tag('jstoolbar/jstoolbar') +
504 512 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
505 513 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
506 514 end
507 515
508 516 def content_for(name, content = nil, &block)
509 517 @has_content ||= {}
510 518 @has_content[name] = true
511 519 super(name, content, &block)
512 520 end
513 521
514 522 def has_content?(name)
515 523 (@has_content && @has_content[name]) || false
516 524 end
517 525 end
@@ -1,105 +1,98
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 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 require 'coderay'
19 require 'coderay/helpers/file_type'
20 18 require 'iconv'
21 19
22 20 module RepositoriesHelper
23 def syntax_highlight(name, content)
24 type = CodeRay::FileType[name]
25 type ? CodeRay.scan(content, type).html : h(content)
26 end
27
28 21 def format_revision(txt)
29 22 txt.to_s[0,8]
30 23 end
31 24
32 25 def to_utf8(str)
33 26 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
34 27 @encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
35 28 @encodings.each do |encoding|
36 29 begin
37 30 return Iconv.conv('UTF-8', encoding, str)
38 31 rescue Iconv::Failure
39 32 # do nothing here and try the next encoding
40 33 end
41 34 end
42 35 str
43 36 end
44 37
45 38 def repository_field_tags(form, repository)
46 39 method = repository.class.name.demodulize.underscore + "_field_tags"
47 40 send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method)
48 41 end
49 42
50 43 def scm_select_tag(repository)
51 44 scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
52 45 REDMINE_SUPPORTED_SCM.each do |scm|
53 46 scm_options << ["Repository::#{scm}".constantize.scm_name, scm] if Setting.enabled_scm.include?(scm) || (repository && repository.class.name.demodulize == scm)
54 47 end
55 48
56 49 select_tag('repository_scm',
57 50 options_for_select(scm_options, repository.class.name.demodulize),
58 51 :disabled => (repository && !repository.new_record?),
59 52 :onchange => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)")
60 53 )
61 54 end
62 55
63 56 def with_leading_slash(path)
64 57 path.to_s.starts_with?('/') ? path : "/#{path}"
65 58 end
66 59
67 60 def without_leading_slash(path)
68 61 path.gsub(%r{^/+}, '')
69 62 end
70 63
71 64 def subversion_field_tags(form, repository)
72 65 content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
73 66 '<br />(http://, https://, svn://, file:///)') +
74 67 content_tag('p', form.text_field(:login, :size => 30)) +
75 68 content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
76 69 :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
77 70 :onfocus => "this.value=''; this.name='repository[password]';",
78 71 :onchange => "this.name='repository[password]';"))
79 72 end
80 73
81 74 def darcs_field_tags(form, repository)
82 75 content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
83 76 end
84 77
85 78 def mercurial_field_tags(form, repository)
86 79 content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
87 80 end
88 81
89 82 def git_field_tags(form, repository)
90 83 content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
91 84 end
92 85
93 86 def cvs_field_tags(form, repository)
94 87 content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
95 88 content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
96 89 end
97 90
98 91 def bazaar_field_tags(form, repository)
99 92 content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
100 93 end
101 94
102 95 def filesystem_field_tags(form, repository)
103 96 content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
104 97 end
105 98 end
@@ -1,118 +1,122
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 "digest/md5"
19 19
20 20 class Attachment < ActiveRecord::Base
21 21 belongs_to :container, :polymorphic => true
22 22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
23 23
24 24 validates_presence_of :container, :filename, :author
25 25 validates_length_of :filename, :maximum => 255
26 26 validates_length_of :disk_filename, :maximum => 255
27 27
28 28 acts_as_event :title => :filename,
29 29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id}}
30 30
31 31 cattr_accessor :storage_path
32 32 @@storage_path = "#{RAILS_ROOT}/files"
33 33
34 34 def validate
35 35 errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes
36 36 end
37 37
38 38 def file=(incoming_file)
39 39 unless incoming_file.nil?
40 40 @temp_file = incoming_file
41 41 if @temp_file.size > 0
42 42 self.filename = sanitize_filename(@temp_file.original_filename)
43 43 self.disk_filename = Attachment.disk_filename(filename)
44 44 self.content_type = @temp_file.content_type.to_s.chomp
45 45 self.filesize = @temp_file.size
46 46 end
47 47 end
48 48 end
49 49
50 50 def file
51 51 nil
52 52 end
53 53
54 54 # Copy temp file to its final location
55 55 def before_save
56 56 if @temp_file && (@temp_file.size > 0)
57 57 logger.debug("saving '#{self.diskfile}'")
58 58 File.open(diskfile, "wb") do |f|
59 59 f.write(@temp_file.read)
60 60 end
61 61 self.digest = Digest::MD5.hexdigest(File.read(diskfile))
62 62 end
63 63 # Don't save the content type if it's longer than the authorized length
64 64 if self.content_type && self.content_type.length > 255
65 65 self.content_type = nil
66 66 end
67 67 end
68 68
69 69 # Deletes file on the disk
70 70 def after_destroy
71 71 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
72 72 end
73 73
74 74 # Returns file's location on disk
75 75 def diskfile
76 76 "#{@@storage_path}/#{self.disk_filename}"
77 77 end
78 78
79 79 def increment_download
80 80 increment!(:downloads)
81 81 end
82 82
83 83 def project
84 84 container.project
85 85 end
86 86
87 87 def image?
88 88 self.filename =~ /\.(jpe?g|gif|png)$/i
89 89 end
90 90
91 def is_text?
92 Redmine::MimeType.is_type?('text', filename)
93 end
94
91 95 def is_diff?
92 96 self.filename =~ /\.(patch|diff)$/i
93 97 end
94 98
95 99 private
96 100 def sanitize_filename(value)
97 101 # get only the filename, not the whole path
98 102 just_filename = value.gsub(/^.*(\\|\/)/, '')
99 103 # NOTE: File.basename doesn't work right with Windows paths on Unix
100 104 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
101 105
102 106 # Finally, replace all non alphanumeric, hyphens or periods with underscore
103 107 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
104 108 end
105 109
106 110 # Returns an ASCII or hashed filename
107 111 def self.disk_filename(filename)
108 112 df = DateTime.now.strftime("%y%m%d%H%M%S") + "_"
109 113 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
110 114 df << filename
111 115 else
112 116 df << Digest::MD5.hexdigest(filename)
113 117 # keep the extension if any
114 118 df << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
115 119 end
116 120 df
117 121 end
118 122 end
@@ -1,39 +1,75
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 container_id: 3
8 8 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
9 9 id: 1
10 10 container_type: Issue
11 11 filesize: 28
12 12 filename: error281.txt
13 13 author_id: 2
14 14 attachments_002:
15 15 created_on: 2006-07-19 21:07:27 +02:00
16 16 downloads: 0
17 17 content_type: text/plain
18 18 disk_filename: 060719210727_document.txt
19 19 container_id: 1
20 20 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
21 21 id: 2
22 22 container_type: Document
23 23 filesize: 28
24 24 filename: document.txt
25 25 author_id: 2
26 26 attachments_003:
27 27 created_on: 2006-07-19 21:07:27 +02:00
28 28 downloads: 0
29 29 content_type: image/gif
30 30 disk_filename: 060719210727_logo.gif
31 31 container_id: 4
32 32 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
33 33 id: 3
34 34 container_type: WikiPage
35 35 filesize: 280
36 36 filename: logo.gif
37 37 description: This is a logo
38 38 author_id: 2
39 No newline at end of file
39 attachments_004:
40 created_on: 2006-07-19 21:07:27 +02:00
41 container_type: Issue
42 container_id: 3
43 downloads: 0
44 disk_filename: 060719210727_source.rb
45 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
46 id: 4
47 filesize: 153
48 filename: source.rb
49 author_id: 2
50 description: This is a Ruby source file
51 content_type: application/x-ruby
52 attachments_005:
53 created_on: 2006-07-19 21:07:27 +02:00
54 container_type: Issue
55 container_id: 3
56 downloads: 0
57 disk_filename: 060719210727_changeset.diff
58 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
59 id: 5
60 filesize: 687
61 filename: changeset.diff
62 author_id: 2
63 content_type: text/x-diff
64 attachments_006:
65 created_on: 2006-07-19 21:07:27 +02:00
66 container_type: Issue
67 container_id: 3
68 downloads: 0
69 disk_filename: 060719210727_archive.zip
70 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
71 id: 6
72 filesize: 157
73 filename: archive.zip
74 author_id: 2
75 content_type: application/octet-stream
@@ -1,64 +1,66
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
19 19 require 'documents_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class DocumentsController; def rescue_action(e) raise e end; end
23 23
24 24 class DocumentsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles, :members, :enabled_modules, :documents, :enumerations
26 26
27 27 def setup
28 28 @controller = DocumentsController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 34 def test_index
35 35 get :index, :project_id => 'ecookbook'
36 36 assert_response :success
37 37 assert_template 'index'
38 38 assert_not_nil assigns(:grouped)
39 39 end
40 40
41 41 def test_new_with_one_attachment
42 42 @request.session[:user_id] = 2
43 set_tmp_attachments_directory
44
43 45 post :new, :project_id => 'ecookbook',
44 46 :document => { :title => 'DocumentsControllerTest#test_post_new',
45 47 :description => 'This is a new document',
46 48 :category_id => 2},
47 49 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
48 50
49 51 assert_redirected_to 'projects/ecookbook/documents'
50 52
51 53 document = Document.find_by_title('DocumentsControllerTest#test_post_new')
52 54 assert_not_nil document
53 55 assert_equal Enumeration.find(2), document.category
54 56 assert_equal 1, document.attachments.size
55 57 assert_equal 'testfile.txt', document.attachments.first.filename
56 58 end
57 59
58 60 def test_destroy
59 61 @request.session[:user_id] = 2
60 62 post :destroy, :id => 1
61 63 assert_redirected_to 'projects/ecookbook/documents'
62 64 assert_nil Document.find_by_id(1)
63 65 end
64 66 end
@@ -1,561 +1,563
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
19 19 require 'issues_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class IssuesController; def rescue_action(e) raise e end; end
23 23
24 24 class IssuesControllerTest < Test::Unit::TestCase
25 25 fixtures :projects,
26 26 :users,
27 27 :roles,
28 28 :members,
29 29 :issues,
30 30 :issue_statuses,
31 31 :trackers,
32 32 :projects_trackers,
33 33 :issue_categories,
34 34 :enabled_modules,
35 35 :enumerations,
36 36 :attachments,
37 37 :workflows,
38 38 :custom_fields,
39 39 :custom_values,
40 40 :custom_fields_trackers,
41 41 :time_entries,
42 42 :journals,
43 43 :journal_details
44 44
45 45 def setup
46 46 @controller = IssuesController.new
47 47 @request = ActionController::TestRequest.new
48 48 @response = ActionController::TestResponse.new
49 49 User.current = nil
50 50 end
51 51
52 52 def test_index
53 53 get :index
54 54 assert_response :success
55 55 assert_template 'index.rhtml'
56 56 assert_not_nil assigns(:issues)
57 57 assert_nil assigns(:project)
58 58 assert_tag :tag => 'a', :content => /Can't print recipes/
59 59 assert_tag :tag => 'a', :content => /Subproject issue/
60 60 # private projects hidden
61 61 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
62 62 assert_no_tag :tag => 'a', :content => /Issue on project 2/
63 63 end
64 64
65 65 def test_index_with_project
66 66 Setting.display_subprojects_issues = 0
67 67 get :index, :project_id => 1
68 68 assert_response :success
69 69 assert_template 'index.rhtml'
70 70 assert_not_nil assigns(:issues)
71 71 assert_tag :tag => 'a', :content => /Can't print recipes/
72 72 assert_no_tag :tag => 'a', :content => /Subproject issue/
73 73 end
74 74
75 75 def test_index_with_project_and_subprojects
76 76 Setting.display_subprojects_issues = 1
77 77 get :index, :project_id => 1
78 78 assert_response :success
79 79 assert_template 'index.rhtml'
80 80 assert_not_nil assigns(:issues)
81 81 assert_tag :tag => 'a', :content => /Can't print recipes/
82 82 assert_tag :tag => 'a', :content => /Subproject issue/
83 83 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
84 84 end
85 85
86 86 def test_index_with_project_and_subprojects_should_show_private_subprojects
87 87 @request.session[:user_id] = 2
88 88 Setting.display_subprojects_issues = 1
89 89 get :index, :project_id => 1
90 90 assert_response :success
91 91 assert_template 'index.rhtml'
92 92 assert_not_nil assigns(:issues)
93 93 assert_tag :tag => 'a', :content => /Can't print recipes/
94 94 assert_tag :tag => 'a', :content => /Subproject issue/
95 95 assert_tag :tag => 'a', :content => /Issue of a private subproject/
96 96 end
97 97
98 98 def test_index_with_project_and_filter
99 99 get :index, :project_id => 1, :set_filter => 1
100 100 assert_response :success
101 101 assert_template 'index.rhtml'
102 102 assert_not_nil assigns(:issues)
103 103 end
104 104
105 105 def test_index_csv_with_project
106 106 get :index, :format => 'csv'
107 107 assert_response :success
108 108 assert_not_nil assigns(:issues)
109 109 assert_equal 'text/csv', @response.content_type
110 110
111 111 get :index, :project_id => 1, :format => 'csv'
112 112 assert_response :success
113 113 assert_not_nil assigns(:issues)
114 114 assert_equal 'text/csv', @response.content_type
115 115 end
116 116
117 117 def test_index_pdf
118 118 get :index, :format => 'pdf'
119 119 assert_response :success
120 120 assert_not_nil assigns(:issues)
121 121 assert_equal 'application/pdf', @response.content_type
122 122
123 123 get :index, :project_id => 1, :format => 'pdf'
124 124 assert_response :success
125 125 assert_not_nil assigns(:issues)
126 126 assert_equal 'application/pdf', @response.content_type
127 127 end
128 128
129 129 def test_changes
130 130 get :changes, :project_id => 1
131 131 assert_response :success
132 132 assert_not_nil assigns(:journals)
133 133 assert_equal 'application/atom+xml', @response.content_type
134 134 end
135 135
136 136 def test_show_by_anonymous
137 137 get :show, :id => 1
138 138 assert_response :success
139 139 assert_template 'show.rhtml'
140 140 assert_not_nil assigns(:issue)
141 141 assert_equal Issue.find(1), assigns(:issue)
142 142
143 143 # anonymous role is allowed to add a note
144 144 assert_tag :tag => 'form',
145 145 :descendant => { :tag => 'fieldset',
146 146 :child => { :tag => 'legend',
147 147 :content => /Notes/ } }
148 148 end
149 149
150 150 def test_show_by_manager
151 151 @request.session[:user_id] = 2
152 152 get :show, :id => 1
153 153 assert_response :success
154 154
155 155 assert_tag :tag => 'form',
156 156 :descendant => { :tag => 'fieldset',
157 157 :child => { :tag => 'legend',
158 158 :content => /Change properties/ } },
159 159 :descendant => { :tag => 'fieldset',
160 160 :child => { :tag => 'legend',
161 161 :content => /Log time/ } },
162 162 :descendant => { :tag => 'fieldset',
163 163 :child => { :tag => 'legend',
164 164 :content => /Notes/ } }
165 165 end
166 166
167 167 def test_get_new
168 168 @request.session[:user_id] = 2
169 169 get :new, :project_id => 1, :tracker_id => 1
170 170 assert_response :success
171 171 assert_template 'new'
172 172
173 173 assert_tag :tag => 'input', :attributes => { :name => 'custom_fields[2]',
174 174 :value => 'Default string' }
175 175 end
176 176
177 177 def test_get_new_without_tracker_id
178 178 @request.session[:user_id] = 2
179 179 get :new, :project_id => 1
180 180 assert_response :success
181 181 assert_template 'new'
182 182
183 183 issue = assigns(:issue)
184 184 assert_not_nil issue
185 185 assert_equal Project.find(1).trackers.first, issue.tracker
186 186 end
187 187
188 188 def test_update_new_form
189 189 @request.session[:user_id] = 2
190 190 xhr :post, :new, :project_id => 1,
191 191 :issue => {:tracker_id => 2,
192 192 :subject => 'This is the test_new issue',
193 193 :description => 'This is the description',
194 194 :priority_id => 5}
195 195 assert_response :success
196 196 assert_template 'new'
197 197 end
198 198
199 199 def test_post_new
200 200 @request.session[:user_id] = 2
201 201 post :new, :project_id => 1,
202 202 :issue => {:tracker_id => 1,
203 203 :subject => 'This is the test_new issue',
204 204 :description => 'This is the description',
205 205 :priority_id => 5,
206 206 :estimated_hours => ''},
207 207 :custom_fields => {'2' => 'Value for field 2'}
208 208 assert_redirected_to 'issues/show'
209 209
210 210 issue = Issue.find_by_subject('This is the test_new issue')
211 211 assert_not_nil issue
212 212 assert_equal 2, issue.author_id
213 213 assert_nil issue.estimated_hours
214 214 v = issue.custom_values.find_by_custom_field_id(2)
215 215 assert_not_nil v
216 216 assert_equal 'Value for field 2', v.value
217 217 end
218 218
219 219 def test_post_new_without_custom_fields_param
220 220 @request.session[:user_id] = 2
221 221 post :new, :project_id => 1,
222 222 :issue => {:tracker_id => 1,
223 223 :subject => 'This is the test_new issue',
224 224 :description => 'This is the description',
225 225 :priority_id => 5}
226 226 assert_redirected_to 'issues/show'
227 227 end
228 228
229 229 def test_copy_issue
230 230 @request.session[:user_id] = 2
231 231 get :new, :project_id => 1, :copy_from => 1
232 232 assert_template 'new'
233 233 assert_not_nil assigns(:issue)
234 234 orig = Issue.find(1)
235 235 assert_equal orig.subject, assigns(:issue).subject
236 236 end
237 237
238 238 def test_get_edit
239 239 @request.session[:user_id] = 2
240 240 get :edit, :id => 1
241 241 assert_response :success
242 242 assert_template 'edit'
243 243 assert_not_nil assigns(:issue)
244 244 assert_equal Issue.find(1), assigns(:issue)
245 245 end
246 246
247 247 def test_get_edit_with_params
248 248 @request.session[:user_id] = 2
249 249 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
250 250 assert_response :success
251 251 assert_template 'edit'
252 252
253 253 issue = assigns(:issue)
254 254 assert_not_nil issue
255 255
256 256 assert_equal 5, issue.status_id
257 257 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
258 258 :child => { :tag => 'option',
259 259 :content => 'Closed',
260 260 :attributes => { :selected => 'selected' } }
261 261
262 262 assert_equal 7, issue.priority_id
263 263 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
264 264 :child => { :tag => 'option',
265 265 :content => 'Urgent',
266 266 :attributes => { :selected => 'selected' } }
267 267 end
268 268
269 269 def test_reply_to_issue
270 270 @request.session[:user_id] = 2
271 271 get :reply, :id => 1
272 272 assert_response :success
273 273 assert_select_rjs :show, "update"
274 274 end
275 275
276 276 def test_reply_to_note
277 277 @request.session[:user_id] = 2
278 278 get :reply, :id => 1, :journal_id => 2
279 279 assert_response :success
280 280 assert_select_rjs :show, "update"
281 281 end
282 282
283 283 def test_post_edit
284 284 @request.session[:user_id] = 2
285 285 ActionMailer::Base.deliveries.clear
286 286
287 287 issue = Issue.find(1)
288 288 old_subject = issue.subject
289 289 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
290 290
291 291 post :edit, :id => 1, :issue => {:subject => new_subject}
292 292 assert_redirected_to 'issues/show/1'
293 293 issue.reload
294 294 assert_equal new_subject, issue.subject
295 295
296 296 mail = ActionMailer::Base.deliveries.last
297 297 assert_kind_of TMail::Mail, mail
298 298 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
299 299 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
300 300 end
301 301
302 302 def test_post_edit_with_status_and_assignee_change
303 303 issue = Issue.find(1)
304 304 assert_equal 1, issue.status_id
305 305 @request.session[:user_id] = 2
306 306 assert_difference('TimeEntry.count', 0) do
307 307 post :edit,
308 308 :id => 1,
309 309 :issue => { :status_id => 2, :assigned_to_id => 3 },
310 310 :notes => 'Assigned to dlopper',
311 311 :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
312 312 end
313 313 assert_redirected_to 'issues/show/1'
314 314 issue.reload
315 315 assert_equal 2, issue.status_id
316 316 j = issue.journals.find(:first, :order => 'id DESC')
317 317 assert_equal 'Assigned to dlopper', j.notes
318 318 assert_equal 2, j.details.size
319 319
320 320 mail = ActionMailer::Base.deliveries.last
321 321 assert mail.body.include?("Status changed from New to Assigned")
322 322 end
323 323
324 324 def test_post_edit_with_note_only
325 325 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
326 326 # anonymous user
327 327 post :edit,
328 328 :id => 1,
329 329 :notes => notes
330 330 assert_redirected_to 'issues/show/1'
331 331 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
332 332 assert_equal notes, j.notes
333 333 assert_equal 0, j.details.size
334 334 assert_equal User.anonymous, j.user
335 335
336 336 mail = ActionMailer::Base.deliveries.last
337 337 assert mail.body.include?(notes)
338 338 end
339 339
340 340 def test_post_edit_with_note_and_spent_time
341 341 @request.session[:user_id] = 2
342 342 spent_hours_before = Issue.find(1).spent_hours
343 343 assert_difference('TimeEntry.count') do
344 344 post :edit,
345 345 :id => 1,
346 346 :notes => '2.5 hours added',
347 347 :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
348 348 end
349 349 assert_redirected_to 'issues/show/1'
350 350
351 351 issue = Issue.find(1)
352 352
353 353 j = issue.journals.find(:first, :order => 'id DESC')
354 354 assert_equal '2.5 hours added', j.notes
355 355 assert_equal 0, j.details.size
356 356
357 357 t = issue.time_entries.find(:first, :order => 'id DESC')
358 358 assert_not_nil t
359 359 assert_equal 2.5, t.hours
360 360 assert_equal spent_hours_before + 2.5, issue.spent_hours
361 361 end
362 362
363 363 def test_post_edit_with_attachment_only
364 set_tmp_attachments_directory
365
364 366 # anonymous user
365 367 post :edit,
366 368 :id => 1,
367 369 :notes => '',
368 370 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
369 371 assert_redirected_to 'issues/show/1'
370 372 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
371 373 assert j.notes.blank?
372 374 assert_equal 1, j.details.size
373 375 assert_equal 'testfile.txt', j.details.first.value
374 376 assert_equal User.anonymous, j.user
375 377
376 378 mail = ActionMailer::Base.deliveries.last
377 379 assert mail.body.include?('testfile.txt')
378 380 end
379 381
380 382 def test_post_edit_with_no_change
381 383 issue = Issue.find(1)
382 384 issue.journals.clear
383 385 ActionMailer::Base.deliveries.clear
384 386
385 387 post :edit,
386 388 :id => 1,
387 389 :notes => ''
388 390 assert_redirected_to 'issues/show/1'
389 391
390 392 issue.reload
391 393 assert issue.journals.empty?
392 394 # No email should be sent
393 395 assert ActionMailer::Base.deliveries.empty?
394 396 end
395 397
396 398 def test_bulk_edit
397 399 @request.session[:user_id] = 2
398 400 # update issues priority
399 401 post :bulk_edit, :ids => [1, 2], :priority_id => 7, :notes => 'Bulk editing', :assigned_to_id => ''
400 402 assert_response 302
401 403 # check that the issues were updated
402 404 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
403 405 assert_equal 'Bulk editing', Issue.find(1).journals.find(:first, :order => 'created_on DESC').notes
404 406 end
405 407
406 408 def test_bulk_unassign
407 409 assert_not_nil Issue.find(2).assigned_to
408 410 @request.session[:user_id] = 2
409 411 # unassign issues
410 412 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
411 413 assert_response 302
412 414 # check that the issues were updated
413 415 assert_nil Issue.find(2).assigned_to
414 416 end
415 417
416 418 def test_move_one_issue_to_another_project
417 419 @request.session[:user_id] = 1
418 420 post :move, :id => 1, :new_project_id => 2
419 421 assert_redirected_to 'projects/ecookbook/issues'
420 422 assert_equal 2, Issue.find(1).project_id
421 423 end
422 424
423 425 def test_bulk_move_to_another_project
424 426 @request.session[:user_id] = 1
425 427 post :move, :ids => [1, 2], :new_project_id => 2
426 428 assert_redirected_to 'projects/ecookbook/issues'
427 429 # Issues moved to project 2
428 430 assert_equal 2, Issue.find(1).project_id
429 431 assert_equal 2, Issue.find(2).project_id
430 432 # No tracker change
431 433 assert_equal 1, Issue.find(1).tracker_id
432 434 assert_equal 2, Issue.find(2).tracker_id
433 435 end
434 436
435 437 def test_bulk_move_to_another_tracker
436 438 @request.session[:user_id] = 1
437 439 post :move, :ids => [1, 2], :new_tracker_id => 2
438 440 assert_redirected_to 'projects/ecookbook/issues'
439 441 assert_equal 2, Issue.find(1).tracker_id
440 442 assert_equal 2, Issue.find(2).tracker_id
441 443 end
442 444
443 445 def test_context_menu_one_issue
444 446 @request.session[:user_id] = 2
445 447 get :context_menu, :ids => [1]
446 448 assert_response :success
447 449 assert_template 'context_menu'
448 450 assert_tag :tag => 'a', :content => 'Edit',
449 451 :attributes => { :href => '/issues/edit/1',
450 452 :class => 'icon-edit' }
451 453 assert_tag :tag => 'a', :content => 'Closed',
452 454 :attributes => { :href => '/issues/edit/1?issue%5Bstatus_id%5D=5',
453 455 :class => '' }
454 456 assert_tag :tag => 'a', :content => 'Immediate',
455 457 :attributes => { :href => '/issues/edit/1?issue%5Bpriority_id%5D=8',
456 458 :class => '' }
457 459 assert_tag :tag => 'a', :content => 'Dave Lopper',
458 460 :attributes => { :href => '/issues/edit/1?issue%5Bassigned_to_id%5D=3',
459 461 :class => '' }
460 462 assert_tag :tag => 'a', :content => 'Copy',
461 463 :attributes => { :href => '/projects/ecookbook/issues/new?copy_from=1',
462 464 :class => 'icon-copy' }
463 465 assert_tag :tag => 'a', :content => 'Move',
464 466 :attributes => { :href => '/issues/move?ids%5B%5D=1',
465 467 :class => 'icon-move' }
466 468 assert_tag :tag => 'a', :content => 'Delete',
467 469 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
468 470 :class => 'icon-del' }
469 471 end
470 472
471 473 def test_context_menu_one_issue_by_anonymous
472 474 get :context_menu, :ids => [1]
473 475 assert_response :success
474 476 assert_template 'context_menu'
475 477 assert_tag :tag => 'a', :content => 'Delete',
476 478 :attributes => { :href => '#',
477 479 :class => 'icon-del disabled' }
478 480 end
479 481
480 482 def test_context_menu_multiple_issues_of_same_project
481 483 @request.session[:user_id] = 2
482 484 get :context_menu, :ids => [1, 2]
483 485 assert_response :success
484 486 assert_template 'context_menu'
485 487 assert_tag :tag => 'a', :content => 'Edit',
486 488 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
487 489 :class => 'icon-edit' }
488 490 assert_tag :tag => 'a', :content => 'Move',
489 491 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
490 492 :class => 'icon-move' }
491 493 assert_tag :tag => 'a', :content => 'Delete',
492 494 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
493 495 :class => 'icon-del' }
494 496 end
495 497
496 498 def test_context_menu_multiple_issues_of_different_project
497 499 @request.session[:user_id] = 2
498 500 get :context_menu, :ids => [1, 2, 4]
499 501 assert_response :success
500 502 assert_template 'context_menu'
501 503 assert_tag :tag => 'a', :content => 'Delete',
502 504 :attributes => { :href => '#',
503 505 :class => 'icon-del disabled' }
504 506 end
505 507
506 508 def test_destroy_issue_with_no_time_entries
507 509 assert_nil TimeEntry.find_by_issue_id(2)
508 510 @request.session[:user_id] = 2
509 511 post :destroy, :id => 2
510 512 assert_redirected_to 'projects/ecookbook/issues'
511 513 assert_nil Issue.find_by_id(2)
512 514 end
513 515
514 516 def test_destroy_issues_with_time_entries
515 517 @request.session[:user_id] = 2
516 518 post :destroy, :ids => [1, 3]
517 519 assert_response :success
518 520 assert_template 'destroy'
519 521 assert_not_nil assigns(:hours)
520 522 assert Issue.find_by_id(1) && Issue.find_by_id(3)
521 523 end
522 524
523 525 def test_destroy_issues_and_destroy_time_entries
524 526 @request.session[:user_id] = 2
525 527 post :destroy, :ids => [1, 3], :todo => 'destroy'
526 528 assert_redirected_to 'projects/ecookbook/issues'
527 529 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
528 530 assert_nil TimeEntry.find_by_id([1, 2])
529 531 end
530 532
531 533 def test_destroy_issues_and_assign_time_entries_to_project
532 534 @request.session[:user_id] = 2
533 535 post :destroy, :ids => [1, 3], :todo => 'nullify'
534 536 assert_redirected_to 'projects/ecookbook/issues'
535 537 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
536 538 assert_nil TimeEntry.find(1).issue_id
537 539 assert_nil TimeEntry.find(2).issue_id
538 540 end
539 541
540 542 def test_destroy_issues_and_reassign_time_entries_to_another_issue
541 543 @request.session[:user_id] = 2
542 544 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
543 545 assert_redirected_to 'projects/ecookbook/issues'
544 546 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
545 547 assert_equal 2, TimeEntry.find(1).issue_id
546 548 assert_equal 2, TimeEntry.find(2).issue_id
547 549 end
548 550
549 551 def test_destroy_attachment
550 552 issue = Issue.find(3)
551 553 a = issue.attachments.size
552 554 @request.session[:user_id] = 2
553 555 post :destroy_attachment, :id => 3, :attachment_id => 1
554 556 assert_redirected_to 'issues/show/3'
555 557 assert_nil Attachment.find_by_id(1)
556 558 issue.reload
557 559 assert_equal((a-1), issue.attachments.size)
558 560 j = issue.journals.find(:first, :order => 'created_on DESC')
559 561 assert_equal 'attachment', j.details.first.property
560 562 end
561 563 end
@@ -1,71 +1,89
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
1 18 require "#{File.dirname(__FILE__)}/../test_helper"
2 19
3 20 class IssuesTest < ActionController::IntegrationTest
4 21 fixtures :projects,
5 22 :users,
6 23 :trackers,
7 24 :projects_trackers,
8 25 :issue_statuses,
9 26 :issues,
10 27 :enumerations,
11 28 :custom_fields,
12 29 :custom_values,
13 30 :custom_fields_trackers
14 31
15 32 # create an issue
16 33 def test_add_issue
17 34 log_user('jsmith', 'jsmith')
18 35 get 'projects/1/issues/new', :tracker_id => '1'
19 36 assert_response :success
20 37 assert_template 'issues/new'
21 38
22 39 post 'projects/1/issues/new', :tracker_id => "1",
23 40 :issue => { :start_date => "2006-12-26",
24 41 :priority_id => "3",
25 42 :subject => "new test issue",
26 43 :category_id => "",
27 44 :description => "new issue",
28 45 :done_ratio => "0",
29 46 :due_date => "",
30 47 :assigned_to_id => "" },
31 48 :custom_fields => {'2' => 'Value for field 2'}
32 49 # find created issue
33 50 issue = Issue.find_by_subject("new test issue")
34 51 assert_kind_of Issue, issue
35 52
36 53 # check redirection
37 54 assert_redirected_to "issues/show"
38 55 follow_redirect!
39 56 assert_equal issue, assigns(:issue)
40 57
41 58 # check issue attributes
42 59 assert_equal 'jsmith', issue.author.login
43 60 assert_equal 1, issue.project.id
44 61 assert_equal 1, issue.status.id
45 62 end
46 63
47 64 # add then remove 2 attachments to an issue
48 65 def test_issue_attachements
49 66 log_user('jsmith', 'jsmith')
67 set_tmp_attachments_directory
50 68
51 69 post 'issues/edit/1',
52 70 :notes => 'Some notes',
53 71 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain'), 'description' => 'This is an attachment'}}
54 72 assert_redirected_to "issues/show/1"
55 73
56 74 # make sure attachment was saved
57 75 attachment = Issue.find(1).attachments.find_by_filename("testfile.txt")
58 76 assert_kind_of Attachment, attachment
59 77 assert_equal Issue.find(1), attachment.container
60 78 assert_equal 'This is an attachment', attachment.description
61 79 # verify the size of the attachment stored in db
62 80 #assert_equal file_data_1.length, attachment.filesize
63 81 # verify that the attachment was written to disk
64 82 assert File.exist?(attachment.diskfile)
65 83
66 84 # remove the attachments
67 85 Issue.find(1).attachments.each(&:destroy)
68 86 assert_equal 0, Issue.find(1).attachments.length
69 87 end
70 88
71 89 end
@@ -1,77 +1,83
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 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 ENV["RAILS_ENV"] ||= "test"
19 19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
20 20 require 'test_help'
21 21 require File.expand_path(File.dirname(__FILE__) + '/helper_testcase')
22 22
23 23 class Test::Unit::TestCase
24 24 # Transactional fixtures accelerate your tests by wrapping each test method
25 25 # in a transaction that's rolled back on completion. This ensures that the
26 26 # test database remains unchanged so your fixtures don't have to be reloaded
27 27 # between every test method. Fewer database queries means faster tests.
28 28 #
29 29 # Read Mike Clark's excellent walkthrough at
30 30 # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
31 31 #
32 32 # Every Active Record database supports transactions except MyISAM tables
33 33 # in MySQL. Turn off transactional fixtures in this case; however, if you
34 34 # don't care one way or the other, switching from MyISAM to InnoDB tables
35 35 # is recommended.
36 36 self.use_transactional_fixtures = true
37 37
38 38 # Instantiated fixtures are slow, but give you @david where otherwise you
39 39 # would need people(:david). If you don't want to migrate your existing
40 40 # test cases which use the @david style and don't mind the speed hit (each
41 41 # instantiated fixtures translates to a database query per test method),
42 42 # then set this back to true.
43 43 self.use_instantiated_fixtures = false
44 44
45 45 # Add more helper methods to be used by all tests here...
46 46
47 47 def log_user(login, password)
48 48 get "/account/login"
49 49 assert_equal nil, session[:user_id]
50 50 assert_response :success
51 51 assert_template "account/login"
52 52 post "/account/login", :username => login, :password => password
53 53 assert_redirected_to "my/page"
54 54 assert_equal login, User.find(session[:user_id]).login
55 55 end
56 56
57 57 def test_uploaded_file(name, mime)
58 58 ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + "/files/#{name}", mime)
59 59 end
60
61 # Use a temporary directory for attachment related tests
62 def set_tmp_attachments_directory
63 Dir.mkdir "#{RAILS_ROOT}/tmp/test/attachments" unless File.directory?("#{RAILS_ROOT}/tmp/test/attachments")
64 Attachment.storage_path = "#{RAILS_ROOT}/tmp/test/attachments"
65 end
60 66 end
61 67
62 68
63 69 # ActionController::TestUploadedFile bug
64 70 # see http://dev.rubyonrails.org/ticket/4635
65 71 class String
66 72 def original_filename
67 73 "testfile.txt"
68 74 end
69 75
70 76 def content_type
71 77 "text/plain"
72 78 end
73 79
74 80 def read
75 81 self.to_s
76 82 end
77 83 end
General Comments 0
You need to be logged in to leave comments. Login now