##// END OF EJS Templates
Unified diff viewer for attached files with .patch or .diff extension (#1403)....
Jean-Philippe Lang -
r1502:d77c1d2829f9
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/diff', :locals => {:diff => @diff, :diff_type => @diff_type} %>
12
13 <% content_for :header_tags do -%>
14 <%= stylesheet_link_tag "scm" -%>
15 <% end -%>
@@ -1,39 +1,46
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AttachmentsController < ApplicationController
18 class AttachmentsController < ApplicationController
19 layout 'base'
19 layout 'base'
20 before_filter :find_project, :check_project_privacy
20 before_filter :find_project, :check_project_privacy
21
21
22 def show
23 if @attachment.is_diff?
24 @diff = File.new(@attachment.diskfile, "rb").read
25 render :action => 'diff'
26 else
27 download
28 end
29 end
30
22 def download
31 def download
23 # images are sent inline
32 # images are sent inline
24 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
33 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
25 :type => @attachment.content_type,
34 :type => @attachment.content_type,
26 :disposition => (@attachment.image? ? 'inline' : 'attachment')
35 :disposition => (@attachment.image? ? 'inline' : 'attachment')
27 rescue
28 # in case the disk file was deleted
29 render_404
30 end
36 end
31
37
32 private
38 private
33 def find_project
39 def find_project
34 @attachment = Attachment.find(params[:id])
40 @attachment = Attachment.find(params[:id])
41 render_404 and return false unless File.readable?(@attachment.diskfile)
35 @project = @attachment.project
42 @project = @attachment.project
36 rescue
43 rescue
37 render_404
44 render_404
38 end
45 end
39 end
46 end
@@ -1,25 +1,29
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module AttachmentsHelper
18 module AttachmentsHelper
19 # displays the links to a collection of attachments
19 # displays the links to a collection of attachments
20 def link_to_attachments(attachments, options = {})
20 def link_to_attachments(attachments, options = {})
21 if attachments.any?
21 if attachments.any?
22 render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options}
22 render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options}
23 end
23 end
24 end
24 end
25
26 def to_utf8(str)
27 str
28 end
25 end
29 end
@@ -1,177 +1,177
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 'csv'
18 require 'csv'
19
19
20 module IssuesHelper
20 module IssuesHelper
21 include ApplicationHelper
21 include ApplicationHelper
22
22
23 def render_issue_tooltip(issue)
23 def render_issue_tooltip(issue)
24 @cached_label_start_date ||= l(:field_start_date)
24 @cached_label_start_date ||= l(:field_start_date)
25 @cached_label_due_date ||= l(:field_due_date)
25 @cached_label_due_date ||= l(:field_due_date)
26 @cached_label_assigned_to ||= l(:field_assigned_to)
26 @cached_label_assigned_to ||= l(:field_assigned_to)
27 @cached_label_priority ||= l(:field_priority)
27 @cached_label_priority ||= l(:field_priority)
28
28
29 link_to_issue(issue) + ": #{h(issue.subject)}<br /><br />" +
29 link_to_issue(issue) + ": #{h(issue.subject)}<br /><br />" +
30 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
30 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
31 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
31 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
32 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
32 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
33 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
33 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
34 end
34 end
35
35
36 def sidebar_queries
36 def sidebar_queries
37 unless @sidebar_queries
37 unless @sidebar_queries
38 # User can see public queries and his own queries
38 # User can see public queries and his own queries
39 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
39 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
40 # Project specific queries and global queries
40 # Project specific queries and global queries
41 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
41 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
42 @sidebar_queries = Query.find(:all,
42 @sidebar_queries = Query.find(:all,
43 :order => "name ASC",
43 :order => "name ASC",
44 :conditions => visible.conditions)
44 :conditions => visible.conditions)
45 end
45 end
46 @sidebar_queries
46 @sidebar_queries
47 end
47 end
48
48
49 def show_detail(detail, no_html=false)
49 def show_detail(detail, no_html=false)
50 case detail.property
50 case detail.property
51 when 'attr'
51 when 'attr'
52 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
52 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
53 case detail.prop_key
53 case detail.prop_key
54 when 'due_date', 'start_date'
54 when 'due_date', 'start_date'
55 value = format_date(detail.value.to_date) if detail.value
55 value = format_date(detail.value.to_date) if detail.value
56 old_value = format_date(detail.old_value.to_date) if detail.old_value
56 old_value = format_date(detail.old_value.to_date) if detail.old_value
57 when 'status_id'
57 when 'status_id'
58 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
58 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
59 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
59 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
60 when 'assigned_to_id'
60 when 'assigned_to_id'
61 u = User.find_by_id(detail.value) and value = u.name if detail.value
61 u = User.find_by_id(detail.value) and value = u.name if detail.value
62 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
62 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
63 when 'priority_id'
63 when 'priority_id'
64 e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
64 e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
65 e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
65 e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
66 when 'category_id'
66 when 'category_id'
67 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
67 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
68 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
68 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
69 when 'fixed_version_id'
69 when 'fixed_version_id'
70 v = Version.find_by_id(detail.value) and value = v.name if detail.value
70 v = Version.find_by_id(detail.value) and value = v.name if detail.value
71 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
71 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
72 end
72 end
73 when 'cf'
73 when 'cf'
74 custom_field = CustomField.find_by_id(detail.prop_key)
74 custom_field = CustomField.find_by_id(detail.prop_key)
75 if custom_field
75 if custom_field
76 label = custom_field.name
76 label = custom_field.name
77 value = format_value(detail.value, custom_field.field_format) if detail.value
77 value = format_value(detail.value, custom_field.field_format) if detail.value
78 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
78 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
79 end
79 end
80 when 'attachment'
80 when 'attachment'
81 label = l(:label_attachment)
81 label = l(:label_attachment)
82 end
82 end
83
83
84 label ||= detail.prop_key
84 label ||= detail.prop_key
85 value ||= detail.value
85 value ||= detail.value
86 old_value ||= detail.old_value
86 old_value ||= detail.old_value
87
87
88 unless no_html
88 unless no_html
89 label = content_tag('strong', label)
89 label = content_tag('strong', label)
90 old_value = content_tag("i", h(old_value)) if detail.old_value
90 old_value = content_tag("i", h(old_value)) if detail.old_value
91 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
91 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
92 if detail.property == 'attachment' && !value.blank? && Attachment.find_by_id(detail.prop_key)
92 if detail.property == 'attachment' && !value.blank? && Attachment.find_by_id(detail.prop_key)
93 # Link to the attachment if it has not been removed
93 # Link to the attachment if it has not been removed
94 value = link_to(value, :controller => 'attachments', :action => 'download', :id => detail.prop_key)
94 value = link_to(value, :controller => 'attachments', :action => 'show', :id => detail.prop_key)
95 else
95 else
96 value = content_tag("i", h(value)) if value
96 value = content_tag("i", h(value)) if value
97 end
97 end
98 end
98 end
99
99
100 if !detail.value.blank?
100 if !detail.value.blank?
101 case detail.property
101 case detail.property
102 when 'attr', 'cf'
102 when 'attr', 'cf'
103 if !detail.old_value.blank?
103 if !detail.old_value.blank?
104 label + " " + l(:text_journal_changed, old_value, value)
104 label + " " + l(:text_journal_changed, old_value, value)
105 else
105 else
106 label + " " + l(:text_journal_set_to, value)
106 label + " " + l(:text_journal_set_to, value)
107 end
107 end
108 when 'attachment'
108 when 'attachment'
109 "#{label} #{value} #{l(:label_added)}"
109 "#{label} #{value} #{l(:label_added)}"
110 end
110 end
111 else
111 else
112 case detail.property
112 case detail.property
113 when 'attr', 'cf'
113 when 'attr', 'cf'
114 label + " " + l(:text_journal_deleted) + " (#{old_value})"
114 label + " " + l(:text_journal_deleted) + " (#{old_value})"
115 when 'attachment'
115 when 'attachment'
116 "#{label} #{old_value} #{l(:label_deleted)}"
116 "#{label} #{old_value} #{l(:label_deleted)}"
117 end
117 end
118 end
118 end
119 end
119 end
120
120
121 def issues_to_csv(issues, project = nil)
121 def issues_to_csv(issues, project = nil)
122 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
122 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
123 export = StringIO.new
123 export = StringIO.new
124 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
124 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
125 # csv header fields
125 # csv header fields
126 headers = [ "#",
126 headers = [ "#",
127 l(:field_status),
127 l(:field_status),
128 l(:field_project),
128 l(:field_project),
129 l(:field_tracker),
129 l(:field_tracker),
130 l(:field_priority),
130 l(:field_priority),
131 l(:field_subject),
131 l(:field_subject),
132 l(:field_assigned_to),
132 l(:field_assigned_to),
133 l(:field_category),
133 l(:field_category),
134 l(:field_fixed_version),
134 l(:field_fixed_version),
135 l(:field_author),
135 l(:field_author),
136 l(:field_start_date),
136 l(:field_start_date),
137 l(:field_due_date),
137 l(:field_due_date),
138 l(:field_done_ratio),
138 l(:field_done_ratio),
139 l(:field_estimated_hours),
139 l(:field_estimated_hours),
140 l(:field_created_on),
140 l(:field_created_on),
141 l(:field_updated_on)
141 l(:field_updated_on)
142 ]
142 ]
143 # Export project custom fields if project is given
143 # Export project custom fields if project is given
144 # otherwise export custom fields marked as "For all projects"
144 # otherwise export custom fields marked as "For all projects"
145 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_custom_fields
145 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_custom_fields
146 custom_fields.each {|f| headers << f.name}
146 custom_fields.each {|f| headers << f.name}
147 # Description in the last column
147 # Description in the last column
148 headers << l(:field_description)
148 headers << l(:field_description)
149 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
149 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
150 # csv lines
150 # csv lines
151 issues.each do |issue|
151 issues.each do |issue|
152 fields = [issue.id,
152 fields = [issue.id,
153 issue.status.name,
153 issue.status.name,
154 issue.project.name,
154 issue.project.name,
155 issue.tracker.name,
155 issue.tracker.name,
156 issue.priority.name,
156 issue.priority.name,
157 issue.subject,
157 issue.subject,
158 issue.assigned_to,
158 issue.assigned_to,
159 issue.category,
159 issue.category,
160 issue.fixed_version,
160 issue.fixed_version,
161 issue.author.name,
161 issue.author.name,
162 format_date(issue.start_date),
162 format_date(issue.start_date),
163 format_date(issue.due_date),
163 format_date(issue.due_date),
164 issue.done_ratio,
164 issue.done_ratio,
165 issue.estimated_hours,
165 issue.estimated_hours,
166 format_time(issue.created_on),
166 format_time(issue.created_on),
167 format_time(issue.updated_on)
167 format_time(issue.updated_on)
168 ]
168 ]
169 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
169 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
170 fields << issue.description
170 fields << issue.description
171 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
171 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
172 end
172 end
173 end
173 end
174 export.rewind
174 export.rewind
175 export
175 export
176 end
176 end
177 end
177 end
@@ -1,114 +1,118
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/md5"
18 require "digest/md5"
19
19
20 class Attachment < ActiveRecord::Base
20 class Attachment < ActiveRecord::Base
21 belongs_to :container, :polymorphic => true
21 belongs_to :container, :polymorphic => true
22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
23
23
24 validates_presence_of :container, :filename, :author
24 validates_presence_of :container, :filename, :author
25 validates_length_of :filename, :maximum => 255
25 validates_length_of :filename, :maximum => 255
26 validates_length_of :disk_filename, :maximum => 255
26 validates_length_of :disk_filename, :maximum => 255
27
27
28 acts_as_event :title => :filename,
28 acts_as_event :title => :filename,
29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id}}
29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id}}
30
30
31 cattr_accessor :storage_path
31 cattr_accessor :storage_path
32 @@storage_path = "#{RAILS_ROOT}/files"
32 @@storage_path = "#{RAILS_ROOT}/files"
33
33
34 def validate
34 def validate
35 errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes
35 errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes
36 end
36 end
37
37
38 def file=(incoming_file)
38 def file=(incoming_file)
39 unless incoming_file.nil?
39 unless incoming_file.nil?
40 @temp_file = incoming_file
40 @temp_file = incoming_file
41 if @temp_file.size > 0
41 if @temp_file.size > 0
42 self.filename = sanitize_filename(@temp_file.original_filename)
42 self.filename = sanitize_filename(@temp_file.original_filename)
43 self.disk_filename = Attachment.disk_filename(filename)
43 self.disk_filename = Attachment.disk_filename(filename)
44 self.content_type = @temp_file.content_type.to_s.chomp
44 self.content_type = @temp_file.content_type.to_s.chomp
45 self.filesize = @temp_file.size
45 self.filesize = @temp_file.size
46 end
46 end
47 end
47 end
48 end
48 end
49
49
50 def file
50 def file
51 nil
51 nil
52 end
52 end
53
53
54 # Copy temp file to its final location
54 # Copy temp file to its final location
55 def before_save
55 def before_save
56 if @temp_file && (@temp_file.size > 0)
56 if @temp_file && (@temp_file.size > 0)
57 logger.debug("saving '#{self.diskfile}'")
57 logger.debug("saving '#{self.diskfile}'")
58 File.open(diskfile, "wb") do |f|
58 File.open(diskfile, "wb") do |f|
59 f.write(@temp_file.read)
59 f.write(@temp_file.read)
60 end
60 end
61 self.digest = Digest::MD5.hexdigest(File.read(diskfile))
61 self.digest = Digest::MD5.hexdigest(File.read(diskfile))
62 end
62 end
63 # Don't save the content type if it's longer than the authorized length
63 # Don't save the content type if it's longer than the authorized length
64 if self.content_type && self.content_type.length > 255
64 if self.content_type && self.content_type.length > 255
65 self.content_type = nil
65 self.content_type = nil
66 end
66 end
67 end
67 end
68
68
69 # Deletes file on the disk
69 # Deletes file on the disk
70 def after_destroy
70 def after_destroy
71 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
71 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
72 end
72 end
73
73
74 # Returns file's location on disk
74 # Returns file's location on disk
75 def diskfile
75 def diskfile
76 "#{@@storage_path}/#{self.disk_filename}"
76 "#{@@storage_path}/#{self.disk_filename}"
77 end
77 end
78
78
79 def increment_download
79 def increment_download
80 increment!(:downloads)
80 increment!(:downloads)
81 end
81 end
82
82
83 def project
83 def project
84 container.project
84 container.project
85 end
85 end
86
86
87 def image?
87 def image?
88 self.filename =~ /\.(jpe?g|gif|png)$/i
88 self.filename =~ /\.(jpe?g|gif|png)$/i
89 end
89 end
90
90
91 def is_diff?
92 self.filename =~ /\.(patch|diff)$/i
93 end
94
91 private
95 private
92 def sanitize_filename(value)
96 def sanitize_filename(value)
93 # get only the filename, not the whole path
97 # get only the filename, not the whole path
94 just_filename = value.gsub(/^.*(\\|\/)/, '')
98 just_filename = value.gsub(/^.*(\\|\/)/, '')
95 # NOTE: File.basename doesn't work right with Windows paths on Unix
99 # NOTE: File.basename doesn't work right with Windows paths on Unix
96 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
100 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
97
101
98 # Finally, replace all non alphanumeric, hyphens or periods with underscore
102 # Finally, replace all non alphanumeric, hyphens or periods with underscore
99 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
103 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
100 end
104 end
101
105
102 # Returns an ASCII or hashed filename
106 # Returns an ASCII or hashed filename
103 def self.disk_filename(filename)
107 def self.disk_filename(filename)
104 df = DateTime.now.strftime("%y%m%d%H%M%S") + "_"
108 df = DateTime.now.strftime("%y%m%d%H%M%S") + "_"
105 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
109 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
106 df << filename
110 df << filename
107 else
111 else
108 df << Digest::MD5.hexdigest(filename)
112 df << Digest::MD5.hexdigest(filename)
109 # keep the extension if any
113 # keep the extension if any
110 df << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
114 df << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
111 end
115 end
112 df
116 df
113 end
117 end
114 end
118 end
@@ -1,18 +1,18
1 <div class="attachments">
1 <div class="attachments">
2 <% for attachment in attachments %>
2 <% for attachment in attachments %>
3 <p><%= link_to attachment.filename, {:controller => 'attachments', :action => 'download', :id => attachment }, :class => 'icon icon-attachment' -%>
3 <p><%= link_to attachment.filename, {:controller => 'attachments', :action => 'show', :id => attachment }, :class => 'icon icon-attachment' -%>
4 <%= h(" - #{attachment.description}") unless attachment.description.blank? %>
4 <%= h(" - #{attachment.description}") unless attachment.description.blank? %>
5 <span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
5 <span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
6 <% if options[:delete_url] %>
6 <% if options[:delete_url] %>
7 <%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}),
7 <%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}),
8 :confirm => l(:text_are_you_sure),
8 :confirm => l(:text_are_you_sure),
9 :method => :post,
9 :method => :post,
10 :class => 'delete',
10 :class => 'delete',
11 :title => l(:button_delete) %>
11 :title => l(:button_delete) %>
12 <% end %>
12 <% end %>
13 <% unless options[:no_author] %>
13 <% unless options[:no_author] %>
14 <span class="author"><%= attachment.author %>, <%= format_time(attachment.created_on) %></span>
14 <span class="author"><%= attachment.author %>, <%= format_time(attachment.created_on) %></span>
15 <% end %>
15 <% end %>
16 </p>
16 </p>
17 <% end %>
17 <% end %>
18 </div>
18 </div>
@@ -1,41 +1,43
1 ActionController::Routing::Routes.draw do |map|
1 ActionController::Routing::Routes.draw do |map|
2 # Add your own custom routes here.
2 # Add your own custom routes here.
3 # The priority is based upon order of creation: first created -> highest priority.
3 # The priority is based upon order of creation: first created -> highest priority.
4
4
5 # Here's a sample route:
5 # Here's a sample route:
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 # Keep in mind you can assign values other than :controller and :action
7 # Keep in mind you can assign values other than :controller and :action
8
8
9 map.home '', :controller => 'welcome'
9 map.home '', :controller => 'welcome'
10 map.signin 'login', :controller => 'account', :action => 'login'
10 map.signin 'login', :controller => 'account', :action => 'login'
11 map.signout 'logout', :controller => 'account', :action => 'logout'
11 map.signout 'logout', :controller => 'account', :action => 'logout'
12
12
13 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
13 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
16 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
16 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
17
17
18 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
18 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
19 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
19 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
20 map.connect 'projects/:project_id/news/:action', :controller => 'news'
20 map.connect 'projects/:project_id/news/:action', :controller => 'news'
21 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
21 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
22 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
22 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
23 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog'
23 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog'
24 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
24 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
25
25
26 map.with_options :controller => 'repositories' do |omap|
26 map.with_options :controller => 'repositories' do |omap|
27 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
27 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
28 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
28 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
29 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
29 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
30 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
30 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
31 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
31 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
32 end
32 end
33
33
34 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show'
35
34 # Allow downloading Web Service WSDL as a file with an extension
36 # Allow downloading Web Service WSDL as a file with an extension
35 # instead of a file named 'wsdl'
37 # instead of a file named 'wsdl'
36 map.connect ':controller/service.wsdl', :action => 'wsdl'
38 map.connect ':controller/service.wsdl', :action => 'wsdl'
37
39
38
40
39 # Install the default route as the lowest priority.
41 # Install the default route as the lowest priority.
40 map.connect ':controller/:action/:id'
42 map.connect ':controller/:action/:id'
41 end
43 end
General Comments 0
You need to be logged in to leave comments. Login now