summaryrefslogtreecommitdiffstats
path: root/app/models/attachment.rb
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2012-12-13 12:07:19 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2012-12-13 12:07:19 +0000
commitc99b638d61cc5dd6b9ffcf4212dfaca1973f7500 (patch)
tree13f809bad3d9ce0c8583b857d5030b55c54bb1f1 /app/models/attachment.rb
parentd0bbaef308ae6183445a8697b08f8eae5ed9ccf8 (diff)
downloadredmine-c99b638d61cc5dd6b9ffcf4212dfaca1973f7500.tar.gz
redmine-c99b638d61cc5dd6b9ffcf4212dfaca1973f7500.zip
Store attachments in subdirectories (#5298).
Existing files can be moved to their target subdirectories using rake redmine:attachments:move_to_subdirectories. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10990 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app/models/attachment.rb')
-rw-r--r--app/models/attachment.rb45
1 files changed, 38 insertions, 7 deletions
diff --git a/app/models/attachment.rb b/app/models/attachment.rb
index 6fa079c91..c288d588b 100644
--- a/app/models/attachment.rb
+++ b/app/models/attachment.rb
@@ -16,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require "digest/md5"
+require "fileutils"
class Attachment < ActiveRecord::Base
belongs_to :container, :polymorphic => true
@@ -92,9 +93,6 @@ class Attachment < ActiveRecord::Base
def filename=(arg)
write_attribute :filename, sanitize_filename(arg.to_s)
- if new_record? && disk_filename.blank?
- self.disk_filename = Attachment.disk_filename(filename)
- end
filename
end
@@ -102,7 +100,13 @@ class Attachment < ActiveRecord::Base
# and computes its MD5 hash
def files_to_final_location
if @temp_file && (@temp_file.size > 0)
+ self.disk_directory = target_directory
+ self.disk_filename = Attachment.disk_filename(filename, disk_directory)
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)")
+ path = File.dirname(diskfile)
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
+ end
md5 = Digest::MD5.new
File.open(diskfile, "wb") do |f|
if @temp_file.respond_to?(:read)
@@ -134,7 +138,7 @@ class Attachment < ActiveRecord::Base
# Returns file's location on disk
def diskfile
- File.join(self.class.storage_path, disk_filename.to_s)
+ File.join(self.class.storage_path, disk_directory.to_s, disk_filename.to_s)
end
def title
@@ -259,6 +263,26 @@ class Attachment < ActiveRecord::Base
Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all
end
+ # Moves an existing attachment to its target directory
+ def move_to_target_directory!
+ if !new_record? & readable?
+ src = diskfile
+ self.disk_directory = target_directory
+ dest = diskfile
+ if src != dest && FileUtils.mkdir_p(File.dirname(dest)) && FileUtils.mv(src, dest)
+ update_column :disk_directory, disk_directory
+ end
+ end
+ end
+
+ # Moves existing attachments that are stored at the root of the files
+ # directory (ie. created before Redmine 2.3) to their target subdirectories
+ def self.move_from_root_to_target_directory
+ Attachment.where("disk_directory IS NULL OR disk_directory = ''").find_each do |attachment|
+ attachment.move_to_target_directory!
+ end
+ end
+
private
# Physically deletes the file from the file system
@@ -276,8 +300,15 @@ class Attachment < ActiveRecord::Base
@filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>]+/, '_')
end
- # Returns an ASCII or hashed filename
- def self.disk_filename(filename)
+ # Returns the subdirectory in which the attachment will be saved
+ def target_directory
+ time = created_on || DateTime.now
+ time.strftime("%Y/%m")
+ end
+
+ # Returns an ASCII or hashed filename that do not
+ # exists yet in the given subdirectory
+ def self.disk_filename(filename, directory=nil)
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
ascii = ''
if filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
@@ -287,7 +318,7 @@ class Attachment < ActiveRecord::Base
# keep the extension if any
ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
end
- while File.exist?(File.join(@@storage_path, "#{timestamp}_#{ascii}"))
+ while File.exist?(File.join(storage_path, directory.to_s, "#{timestamp}_#{ascii}"))
timestamp.succ!
end
"#{timestamp}_#{ascii}"