##// END OF EJS Templates
Unused variable...
Eric Davis -
r3660:f08b2a394d73
parent child
Show More
@@ -1,192 +1,191
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/md5"
18 require "digest/md5"
19
19
20 class Attachment < ActiveRecord::Base
20 class Attachment < ActiveRecord::Base
21 belongs_to :container, :polymorphic => true
21 belongs_to :container, :polymorphic => true
22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
22 belongs_to :author, :class_name => "User", :foreign_key => "author_id"
23
23
24 validates_presence_of :container, :filename, :author
24 validates_presence_of :container, :filename, :author
25 validates_length_of :filename, :maximum => 255
25 validates_length_of :filename, :maximum => 255
26 validates_length_of :disk_filename, :maximum => 255
26 validates_length_of :disk_filename, :maximum => 255
27
27
28 acts_as_event :title => :filename,
28 acts_as_event :title => :filename,
29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
29 :url => Proc.new {|o| {:controller => 'attachments', :action => 'download', :id => o.id, :filename => o.filename}}
30
30
31 acts_as_activity_provider :type => 'files',
31 acts_as_activity_provider :type => 'files',
32 :permission => :view_files,
32 :permission => :view_files,
33 :author_key => :author_id,
33 :author_key => :author_id,
34 :find_options => {:select => "#{Attachment.table_name}.*",
34 :find_options => {:select => "#{Attachment.table_name}.*",
35 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
35 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
36 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
36 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
37
37
38 acts_as_activity_provider :type => 'documents',
38 acts_as_activity_provider :type => 'documents',
39 :permission => :view_documents,
39 :permission => :view_documents,
40 :author_key => :author_id,
40 :author_key => :author_id,
41 :find_options => {:select => "#{Attachment.table_name}.*",
41 :find_options => {:select => "#{Attachment.table_name}.*",
42 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
42 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
43 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
43 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
44
44
45 cattr_accessor :storage_path
45 cattr_accessor :storage_path
46 @@storage_path = "#{RAILS_ROOT}/files"
46 @@storage_path = "#{RAILS_ROOT}/files"
47
47
48 def validate
48 def validate
49 if self.filesize > Setting.attachment_max_size.to_i.kilobytes
49 if self.filesize > Setting.attachment_max_size.to_i.kilobytes
50 errors.add(:base, :too_long, :count => Setting.attachment_max_size.to_i.kilobytes)
50 errors.add(:base, :too_long, :count => Setting.attachment_max_size.to_i.kilobytes)
51 end
51 end
52 end
52 end
53
53
54 def file=(incoming_file)
54 def file=(incoming_file)
55 unless incoming_file.nil?
55 unless incoming_file.nil?
56 @temp_file = incoming_file
56 @temp_file = incoming_file
57 if @temp_file.size > 0
57 if @temp_file.size > 0
58 self.filename = sanitize_filename(@temp_file.original_filename)
58 self.filename = sanitize_filename(@temp_file.original_filename)
59 self.disk_filename = Attachment.disk_filename(filename)
59 self.disk_filename = Attachment.disk_filename(filename)
60 self.content_type = @temp_file.content_type.to_s.chomp
60 self.content_type = @temp_file.content_type.to_s.chomp
61 if content_type.blank?
61 if content_type.blank?
62 self.content_type = Redmine::MimeType.of(filename)
62 self.content_type = Redmine::MimeType.of(filename)
63 end
63 end
64 self.filesize = @temp_file.size
64 self.filesize = @temp_file.size
65 end
65 end
66 end
66 end
67 end
67 end
68
68
69 def file
69 def file
70 nil
70 nil
71 end
71 end
72
72
73 # Copies the temporary file to its final location
73 # Copies the temporary file to its final location
74 # and computes its MD5 hash
74 # and computes its MD5 hash
75 def before_save
75 def before_save
76 if @temp_file && (@temp_file.size > 0)
76 if @temp_file && (@temp_file.size > 0)
77 logger.debug("saving '#{self.diskfile}'")
77 logger.debug("saving '#{self.diskfile}'")
78 md5 = Digest::MD5.new
78 md5 = Digest::MD5.new
79 File.open(diskfile, "wb") do |f|
79 File.open(diskfile, "wb") do |f|
80 buffer = ""
80 buffer = ""
81 while (buffer = @temp_file.read(8192))
81 while (buffer = @temp_file.read(8192))
82 f.write(buffer)
82 f.write(buffer)
83 md5.update(buffer)
83 md5.update(buffer)
84 end
84 end
85 end
85 end
86 self.digest = md5.hexdigest
86 self.digest = md5.hexdigest
87 end
87 end
88 # Don't save the content type if it's longer than the authorized length
88 # Don't save the content type if it's longer than the authorized length
89 if self.content_type && self.content_type.length > 255
89 if self.content_type && self.content_type.length > 255
90 self.content_type = nil
90 self.content_type = nil
91 end
91 end
92 end
92 end
93
93
94 # Deletes file on the disk
94 # Deletes file on the disk
95 def after_destroy
95 def after_destroy
96 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
96 File.delete(diskfile) if !filename.blank? && File.exist?(diskfile)
97 end
97 end
98
98
99 # Returns file's location on disk
99 # Returns file's location on disk
100 def diskfile
100 def diskfile
101 "#{@@storage_path}/#{self.disk_filename}"
101 "#{@@storage_path}/#{self.disk_filename}"
102 end
102 end
103
103
104 def increment_download
104 def increment_download
105 increment!(:downloads)
105 increment!(:downloads)
106 end
106 end
107
107
108 def project
108 def project
109 container.project
109 container.project
110 end
110 end
111
111
112 def visible?(user=User.current)
112 def visible?(user=User.current)
113 container.attachments_visible?(user)
113 container.attachments_visible?(user)
114 end
114 end
115
115
116 def deletable?(user=User.current)
116 def deletable?(user=User.current)
117 container.attachments_deletable?(user)
117 container.attachments_deletable?(user)
118 end
118 end
119
119
120 def image?
120 def image?
121 self.filename =~ /\.(jpe?g|gif|png)$/i
121 self.filename =~ /\.(jpe?g|gif|png)$/i
122 end
122 end
123
123
124 def is_text?
124 def is_text?
125 Redmine::MimeType.is_type?('text', filename)
125 Redmine::MimeType.is_type?('text', filename)
126 end
126 end
127
127
128 def is_diff?
128 def is_diff?
129 self.filename =~ /\.(patch|diff)$/i
129 self.filename =~ /\.(patch|diff)$/i
130 end
130 end
131
131
132 # Returns true if the file is readable
132 # Returns true if the file is readable
133 def readable?
133 def readable?
134 File.readable?(diskfile)
134 File.readable?(diskfile)
135 end
135 end
136
136
137 # Bulk attaches a set of files to an object
137 # Bulk attaches a set of files to an object
138 #
138 #
139 # Returns a Hash of the results:
139 # Returns a Hash of the results:
140 # :files => array of the attached files
140 # :files => array of the attached files
141 # :unsaved => array of the files that could not be attached
141 # :unsaved => array of the files that could not be attached
142 def self.attach_files(obj, attachments)
142 def self.attach_files(obj, attachments)
143 attached = []
143 attached = []
144 unsaved = []
145 if attachments && attachments.is_a?(Hash)
144 if attachments && attachments.is_a?(Hash)
146 attachments.each_value do |attachment|
145 attachments.each_value do |attachment|
147 file = attachment['file']
146 file = attachment['file']
148 next unless file && file.size > 0
147 next unless file && file.size > 0
149 a = Attachment.create(:container => obj,
148 a = Attachment.create(:container => obj,
150 :file => file,
149 :file => file,
151 :description => attachment['description'].to_s.strip,
150 :description => attachment['description'].to_s.strip,
152 :author => User.current)
151 :author => User.current)
153
152
154 if a.new_record?
153 if a.new_record?
155 obj.unsaved_attachments ||= []
154 obj.unsaved_attachments ||= []
156 obj.unsaved_attachments << a
155 obj.unsaved_attachments << a
157 else
156 else
158 attached << a
157 attached << a
159 end
158 end
160 end
159 end
161 end
160 end
162 {:files => attached, :unsaved => obj.unsaved_attachments}
161 {:files => attached, :unsaved => obj.unsaved_attachments}
163 end
162 end
164
163
165 private
164 private
166 def sanitize_filename(value)
165 def sanitize_filename(value)
167 # get only the filename, not the whole path
166 # get only the filename, not the whole path
168 just_filename = value.gsub(/^.*(\\|\/)/, '')
167 just_filename = value.gsub(/^.*(\\|\/)/, '')
169 # NOTE: File.basename doesn't work right with Windows paths on Unix
168 # NOTE: File.basename doesn't work right with Windows paths on Unix
170 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
169 # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
171
170
172 # Finally, replace all non alphanumeric, hyphens or periods with underscore
171 # Finally, replace all non alphanumeric, hyphens or periods with underscore
173 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
172 @filename = just_filename.gsub(/[^\w\.\-]/,'_')
174 end
173 end
175
174
176 # Returns an ASCII or hashed filename
175 # Returns an ASCII or hashed filename
177 def self.disk_filename(filename)
176 def self.disk_filename(filename)
178 timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
177 timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
179 ascii = ''
178 ascii = ''
180 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
179 if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
181 ascii = filename
180 ascii = filename
182 else
181 else
183 ascii = Digest::MD5.hexdigest(filename)
182 ascii = Digest::MD5.hexdigest(filename)
184 # keep the extension if any
183 # keep the extension if any
185 ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
184 ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
186 end
185 end
187 while File.exist?(File.join(@@storage_path, "#{timestamp}_#{ascii}"))
186 while File.exist?(File.join(@@storage_path, "#{timestamp}_#{ascii}"))
188 timestamp.succ!
187 timestamp.succ!
189 end
188 end
190 "#{timestamp}_#{ascii}"
189 "#{timestamp}_#{ascii}"
191 end
190 end
192 end
191 end
General Comments 0
You need to be logged in to leave comments. Login now