Document Location - Folder Structure #543

This commit is contained in:
Karel Picman 2017-04-19 11:56:29 +02:00
parent 659427b81d
commit 1a14bae5ce
14 changed files with 223 additions and 76 deletions

View File

@ -254,34 +254,20 @@ class DmsfFile < ActiveRecord::Base
errors[:base] << l(:error_file_is_locked)
return false
end
project = container.is_a?(Project) ? container : container.project
# If the target project differs from the source project we must physically move the disk files
if self.project != project
self.dmsf_file_revisions.all.each do |rev|
if File.exist? rev.disk_file(self.project)
FileUtils.mv rev.disk_file(self.project), rev.disk_file(project)
end
end
end
# Must invalidate source parent folder cache before moving
RedmineDmsf::Webdav::Cache.invalidate_item(propfind_cache_key)
self.container_type = self.container_type
self.container_id = container.id
self.dmsf_folder = folder
new_revision = self.last_revision.clone
new_revision.dmsf_file = self
project = container.is_a?(Project) ? container : container.project
new_revision.comment = l(:comment_moved_from, :source => "#{self.project.identifier}:#{self.dmsf_path_str}")
new_revision.custom_values = []
self.last_revision.custom_values.each do |cv|
new_revision.custom_values << CustomValue.new({:custom_field => cv.custom_field, :value => cv.value})
end
self.set_last_revision(new_revision)
self.save && new_revision.save
end
@ -290,13 +276,6 @@ class DmsfFile < ActiveRecord::Base
end
def copy_to_filename(container, folder=nil, filename)
project = container.is_a?(Project) ? container : container.project
# If the target project differs from the source project we must physically move the disk files
if (self.project != project) && self.last_revision
if File.exist? self.last_revision.disk_file(self.project)
FileUtils.cp self.last_revision.disk_file(self.project), self.last_revision.disk_file(project)
end
end
file = DmsfFile.new
file.dmsf_folder = folder
file.container_type = self.container_type
@ -306,6 +285,11 @@ class DmsfFile < ActiveRecord::Base
if file.save && self.last_revision
new_revision = self.last_revision.clone
new_revision.dmsf_file = file
new_revision.disk_filename = new_revision.new_storage_filename
if File.exist? self.last_revision.disk_file
FileUtils.cp self.last_revision.disk_file, new_revision.disk_file
end
project = container.is_a?(Project) ? container : container.project
new_revision.comment = l(:comment_copied_from, :source => "#{project.identifier}: #{self.dmsf_path_str}")
new_revision.custom_values = []
self.last_revision.custom_values.each do |cv|

View File

@ -132,22 +132,28 @@ class DmsfFileRevision < ActiveRecord::Base
"#{self.major_version}.#{self.minor_version}"
end
def storage_base_path(project = nil)
project = self.dmsf_file.project unless project
path = DmsfFile.storage_path.dup
if self.dmsf_file && project
project_base = project.identifier.gsub(/[^\w\.\-]/,'_')
path << "/p_#{project_base}"
end
def storage_base_path
time = self.created_at || DateTime.now
path = time.strftime('%Y/%m')
"#{DmsfFile.storage_path}/#{path}"
end
def disk_file(project = nil)
project = self.dmsf_file.project unless project
path = storage_base_path(project)
def disk_file
path = self.storage_base_path
FileUtils.mkdir_p(path) unless File.exist?(path)
"#{path}/#{self.disk_filename}"
end
def new_storage_filename
raise DmsfAccessError, 'File id is not set' unless self.dmsf_file.id
filename = DmsfHelper.sanitize_filename(self.name)
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
while File.exist?(File.join(storage_base_path, "#{timestamp}_#{self.dmsf_file.id}_#{filename}"))
timestamp.succ!
end
"#{timestamp}_#{self.dmsf_file.id}_#{filename}"
end
def detect_content_type
content_type = self.mime_type
content_type = Redmine::MimeType.of(self.disk_filename) if content_type.blank?
@ -228,16 +234,6 @@ class DmsfFileRevision < ActiveRecord::Base
end
end
def new_storage_filename
raise DmsfAccessError, 'File id is not set' unless self.dmsf_file.id
filename = DmsfHelper.sanitize_filename(self.name)
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
while File.exist?(File.join(storage_base_path, "#{timestamp}_#{self.dmsf_file.id}_#{filename}"))
timestamp.succ!
end
"#{timestamp}_#{self.dmsf_file.id}_#{filename}"
end
def copy_file_content(open_file)
File.open(self.disk_file, 'wb') do |f|
while (buffer = open_file.read(8192))

View File

@ -297,6 +297,10 @@ class DmsfFolder < ActiveRecord::Base
l.copy_to project, new_folder
end
self.dmsf_folder_permissions.each do |p|
p.copy_to new_folder
end
return new_folder
end

View File

@ -25,4 +25,14 @@ class DmsfFolderPermission < ActiveRecord::Base
scope :users, -> { where(:object_type => User.model_name.to_s) }
scope :roles, -> { where(:object_type => Role.model_name.to_s) }
def copy_to(folder)
permission = DmsfFolderPermission.new
permission.dmsf_folder_id = folder.id
permission.object_id = self.object_id
permission.object_type = self.object_type
permission.save
permission
end
end

View File

@ -110,14 +110,14 @@ class DmsfLink < ActiveRecord::Base
end
def copy_to(project, folder)
link = DmsfLink.new(
:target_project_id => self.target_project_id,
:target_id => self.target_id,
:target_type => self.target_type,
:name => self.name,
:external_url => self.external_url,
:project_id => project.id,
:dmsf_folder_id => folder ? folder.id : nil)
link = DmsfLink.new
link.target_project_id = self.target_project_id
link.target_id = self.target_id
link.target_type = self.target_type
link.name = self.name
link.external_url = self.external_url
link.project_id = project.id
link.dmsf_folder_id = folder ? folder.id : nil
link.save
link
end

View File

@ -0,0 +1,120 @@
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-17 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class MigrateDocuments < ActiveRecord::Migration
def up
# Migrate all documents from dmsf/p_{project identifier} to dmsf/{year}/{month}
DmsfFileRevision.find_each do |dmsf_file_revision|
if dmsf_file_revision.dmsf_file
if dmsf_file_revision.dmsf_file.project
origin = self.disk_file(dmsf_file_revision)
if origin
if File.exist?(origin)
target = dmsf_file_revision.disk_file
if target
unless File.exist?(target)
begin
FileUtils.mv origin, target, :verbose => true
folder = self.storage_base_path(dmsf_file_revision)
Dir.rmdir(folder) if (folder && (Dir.entries(folder).size == 2))
rescue Exception => e
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: #{e.message}"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: Target '#{target}' exists"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: target = nil"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: Origin '#{origin}' doesn't exist"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: disk_file = nil"
end
else
Rails.logger.error "DmsfFile ID #{dmsf_file_revision.dmsf_file.id}: project = nil"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: dmsf_file = nil"
end
end
end
def down
# Migrate all documents from dmsf/p_{project identifier} to dmsf/{year}/{month}
DmsfFileRevision.find_each do |dmsf_file_revision|
if dmsf_file_revision.dmsf_file
if dmsf_file_revision.dmsf_file.project
origin = dmsf_file_revision.disk_file
if origin
if File.exist?(origin)
target = self.disk_file(dmsf_file_revision)
if target
unless File.exist?(target)
begin
FileUtils.mv origin, target, :verbose => true
folder = dmsf_file_revision.storage_base_path
Dir.rmdir(folder) if (folder && (Dir.entries(folder).size == 2))
rescue Exception => e
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: #{e.message}"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: Target '#{target}' exists"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: target = nil"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: Origin '#{origin}' doesn't exist"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: disk_file = nil"
end
else
Rails.logger.error "DmsfFile ID #{dmsf_file_revision.dmsf_file.id}: project = nil"
end
else
Rails.logger.error "DmsfFileRevisions ID #{dmsf_file_revision.id}: dmsf_file = nil"
end
end
end
def storage_base_path(dmsf_file_revision)
if dmsf_file_revision.dmsf_file
if dmsf_file_revision.dmsf_file.project
project_base = dmsf_file_revision.dmsf_file.project.identifier.gsub(/[^\w\.\-]/,'_')
return "#{DmsfFile.storage_path}/p_#{project_base}"
end
end
nil
end
def disk_file(dmsf_file_revision)
path = storage_base_path(dmsf_file_revision)
if path
FileUtils.mkdir_p(path) unless File.exist?(path)
return "#{path}/#{dmsf_file_revision.disk_filename}"
end
nil
end
end

View File

@ -17,7 +17,8 @@ dmsf_file_revisions_001:
deleted_by_user_id: NULL
user_id: 1
dmsf_workflow_assigned_by: 1
dmsf_workflow_started_by: 1
dmsf_workflow_started_by: 1
created_at: 2017-04-18 14:52:27 +02:00
#revision for file on non-enabled project
dmsf_file_revisions_002:
@ -38,7 +39,8 @@ dmsf_file_revisions_002:
deleted_by_user_id: NULL
user_id: 1
dmsf_workflow_assigned_by: 1
dmsf_workflow_started_by: 1
dmsf_workflow_started_by: 1
created_at: 2017-04-18 14:52:27 +02:00
#revision for deleted file on dmsf-enabled project
dmsf_file_revisions_003:
@ -59,7 +61,8 @@ dmsf_file_revisions_003:
deleted_by_user_id: 1
user_id: 1
dmsf_workflow_assigned_by: 1
dmsf_workflow_started_by: 1
dmsf_workflow_started_by: 1
created_at: 2017-04-18 14:52:27 +02:00
dmsf_file_revisions_004:
id: 4
@ -79,7 +82,8 @@ dmsf_file_revisions_004:
deleted_by_user_id: NULL
user_id: 1
dmsf_workflow_assigned_by: NULL
dmsf_workflow_started_by: NULL
dmsf_workflow_started_by: NULL
created_at: 2017-04-18 14:52:27 +02:00
dmsf_file_revisions_005:
id: 5
@ -100,6 +104,7 @@ dmsf_file_revisions_005:
user_id: 1
dmsf_workflow_assigned_by: NULL
dmsf_workflow_started_by: NULL
created_at: 2017-04-18 14:52:27 +02:00
dmsf_file_revisions_006:
id: 6
@ -120,6 +125,7 @@ dmsf_file_revisions_006:
user_id: 1
dmsf_workflow_assigned_by: NULL
dmsf_workflow_started_by: NULL
created_at: 2017-04-18 14:52:27 +02:00
dmsf_file_revisions_007:
id: 7
@ -140,6 +146,7 @@ dmsf_file_revisions_007:
user_id: 1
dmsf_workflow_assigned_by: NULL
dmsf_workflow_started_by: NULL
created_at: 2017-04-18 14:52:27 +02:00
dmsf_file_revisions_008:
id: 8
@ -160,6 +167,7 @@ dmsf_file_revisions_008:
user_id: 1
dmsf_workflow_assigned_by: NULL
dmsf_workflow_started_by: NULL
created_at: 2017-04-18 14:52:27 +02:00
dmsf_file_revisions_009:
id: 9
@ -180,5 +188,6 @@ dmsf_file_revisions_009:
user_id: 1
dmsf_workflow_assigned_by: NULL
dmsf_workflow_started_by: NULL
created_at: 2017-04-18 14:52:27 +02:00

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -1 +0,0 @@
1234

View File

@ -21,7 +21,17 @@
require File.expand_path('../../test_helper', __FILE__)
class DmsfFolderPermissionTest < RedmineDmsf::Test::UnitTest
fixtures :dmsf_folder_permissions
fixtures :dmsf_folder_permissions, :dmsf_folders
def setup
@folder1 = DmsfFolder.find 1
@permission1 = DmsfFolderPermission.find 1
end
def test_truth
assert_kind_of DmsfFolder, @folder1
assert_kind_of DmsfFolderPermission, @permission1
end
def test_scope
assert_equal 2, DmsfFolderPermission.count
@ -35,4 +45,12 @@ class DmsfFolderPermissionTest < RedmineDmsf::Test::UnitTest
assert_equal 1, DmsfFolderPermission.roles.count
end
def test_copy_to
permission = @permission1.copy_to(@folder1)
assert permission
assert_equal @folder1.id, permission.dmsf_folder_id
assert_equal @permission1.object_id, permission.object_id
assert_equal @permission1.object_type, permission.object_type
end
end

View File

@ -34,6 +34,10 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
@folder7 = DmsfFolder.find_by_id 7
@manager = User.find_by_id 2
@developer = User.find_by_id 3
manager_role = Role.find 1
manager_role.add_permission! :view_dmsf_folders
developer_role = Role.find 2
developer_role.add_permission! :view_dmsf_folders
end
def test_truth
@ -49,13 +53,14 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
def test_visiblity
# The role has got permissions
User.current = @manager
assert_equal 6, DmsfFolder.visible.where(:project_id => 1).count
assert_equal 5, DmsfFolder.where(:project_id => 1).count
assert_equal 5, DmsfFolder.visible.where(:project_id => 1).count
# The user has got permissions
User.current = @developer
assert_equal 6, DmsfFolder.visible.where(:project_id => 1).count
assert_equal 5, DmsfFolder.visible.where(:project_id => 1).count
# Hasn't got permissions for @folder7
@folder7.dmsf_folder_permissions.where(:object_type => 'User').delete_all
assert_equal 5, DmsfFolder.visible.where(:project_id => 1).count
assert_equal 4, DmsfFolder.visible.where(:project_id => 1).count
end
def test_permissions
@ -65,22 +70,22 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
@folder7.reload
assert !DmsfFolder.permissions(@folder7)
end
def test_delete
def test_delete
assert @folder6.delete(false), @folder6.errors.full_messages.to_sentence
assert @folder6.deleted?, "Folder #{@folder6} hasn't been deleted"
end
def test_restore
def test_restore
assert @folder6.delete(false), @folder6.errors.full_messages.to_sentence
assert @folder6.deleted?, "Folder #{@folder6} hasn't been deleted"
assert @folder6.restore, @folder6.errors.full_messages.to_sentence
assert !@folder6.deleted?, "Folder #{@folder6} hasn't been restored"
end
def test_destroy
def test_destroy
@folder6.delete true
assert_nil DmsfFolder.find_by_id(@folder6.id)
assert_nil DmsfFolder.find_by_id(@folder6.id)
end
def test_is_column_on_default
@ -94,7 +99,7 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
assert !DmsfFolder.is_column_on?(column), "The column #{column} is on?"
end
end
def test_get_column_position_default
# 0 - checkbox
assert_nil DmsfFolder.get_column_position('checkbox'), "The column 'checkbox' is on?"
@ -133,7 +138,7 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
def test_save_and_destroy_with_cache
RedmineDmsf::Webdav::Cache.init_testcache
# save
cache_key = @folder4.propfind_cache_key
RedmineDmsf::Webdav::Cache.write(cache_key, "")
@ -143,7 +148,7 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
assert !RedmineDmsf::Webdav::Cache.exist?(cache_key)
assert RedmineDmsf::Webdav::Cache.exist?("#{cache_key}.invalid")
RedmineDmsf::Webdav::Cache.delete("#{cache_key}.invalid")
# destroy
RedmineDmsf::Webdav::Cache.write(cache_key, "")
assert RedmineDmsf::Webdav::Cache.exist?(cache_key)
@ -152,7 +157,7 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
assert !RedmineDmsf::Webdav::Cache.exist?(cache_key)
assert RedmineDmsf::Webdav::Cache.exist?("#{cache_key}.invalid")
RedmineDmsf::Webdav::Cache.cache.clear
# save!
cache_key = @folder5.propfind_cache_key
RedmineDmsf::Webdav::Cache.write(cache_key, "")
@ -162,7 +167,7 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
assert !RedmineDmsf::Webdav::Cache.exist?(cache_key)
assert RedmineDmsf::Webdav::Cache.exist?("#{cache_key}.invalid")
RedmineDmsf::Webdav::Cache.delete("#{cache_key}.invalid")
# destroy!
RedmineDmsf::Webdav::Cache.write(cache_key, "")
assert RedmineDmsf::Webdav::Cache.exist?(cache_key)
@ -170,7 +175,7 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest
@folder5.destroy!
assert !RedmineDmsf::Webdav::Cache.exist?(cache_key)
assert RedmineDmsf::Webdav::Cache.exist?("#{cache_key}.invalid")
RedmineDmsf::Webdav::Cache.init_nullcache
end

View File

@ -27,6 +27,8 @@ class ProjectPatchTest < RedmineDmsf::Test::UnitTest
@project1 = Project.find_by_id 1
@project2 = Project.find_by_id 2
@project3 = Project.find_by_id 3
admin = User.find 1
User.current = admin
end
def test_truth
@ -66,7 +68,7 @@ class ProjectPatchTest < RedmineDmsf::Test::UnitTest
def test_dmsf_count
hash = @project1.dmsf_count
assert_equal 7, hash[:files]
assert_equal 7, hash[:folders]
assert_equal 6, hash[:folders]
end
def test_copy_approval_workflows
@ -78,18 +80,18 @@ class ProjectPatchTest < RedmineDmsf::Test::UnitTest
def test_copy_dmsf
assert_equal 3, @project1.dmsf_files.visible.count
assert_equal 4, @project1.dmsf_folders.visible.count
assert_equal 3, @project1.dmsf_folders.visible.count
assert_equal 1, @project1.file_links.visible.count
assert_equal 1, @project1.folder_links.visible.count
assert_equal 1, @project1.url_links.visible.count
assert_equal 0, @project3.dmsf_files.visible.count
assert_equal 0, @project3.dmsf_folders.visible.count
assert_equal 0, @project3.dmsf_folders.count
assert_equal 0, @project3.file_links.visible.count
assert_equal 0, @project3.folder_links.visible.count
assert_equal 0, @project3.url_links.visible.count
@project3.copy_dmsf(@project1)
assert_equal 3, @project3.dmsf_files.visible.count
assert_equal 3, @project3.dmsf_folders.visible.count # Folder if 7 is not visible due to folder permissions
assert_equal 3, @project3.dmsf_folders.count
assert_equal 1, @project3.file_links.visible.count
assert_equal 1, @project3.folder_links.visible.count
assert_equal 1, @project3.url_links.visible.count