diff app/models/attachment.rb @ 1295:622f24f53b42 redmine-2.3

Update to Redmine SVN revision 11972 on 2.3-stable branch
author Chris Cannam
date Fri, 14 Jun 2013 09:02:21 +0100
parents 433d4f72a19b
children
line wrap: on
line diff
--- a/app/models/attachment.rb	Fri Jun 14 09:01:12 2013 +0100
+++ b/app/models/attachment.rb	Fri Jun 14 09:02:21 2013 +0100
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2012  Jean-Philippe Lang
+# Copyright (C) 2006-2013  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -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 @@
 
   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 @@
   # 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 @@
 
   # 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
@@ -154,11 +158,19 @@
   end
 
   def visible?(user=User.current)
-    container && container.attachments_visible?(user)
+    if container_id
+      container && container.attachments_visible?(user)
+    else
+      author == user
+    end
   end
 
   def deletable?(user=User.current)
-    container && container.attachments_deletable?(user)
+    if container_id
+      container && container.attachments_deletable?(user)
+    else
+      author == user
+    end
   end
 
   def image?
@@ -251,6 +263,26 @@
     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
@@ -268,8 +300,15 @@
     @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_\.\-]*$}
@@ -279,7 +318,7 @@
       # 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}"