diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index 087f48b3..550c7756 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -506,7 +506,7 @@ def email_entries(selected_folders, selected_files) raise RedmineDmsf::Errors::DmsfEmailMaxFileSizeError end - zip.files.each do |f| + zip.dmsf_files.each do |f| # Action audit = DmsfFileRevisionAccess.new audit.user = User.current @@ -517,7 +517,7 @@ def email_entries(selected_folders, selected_files) # Notification begin - DmsfMailer.deliver_files_downloaded(@project, zip.files, request.remote_ip) + DmsfMailer.deliver_files_downloaded @project, zip.dmsf_files, request.remote_ip rescue StandardError => e Rails.logger.error "Could not send email notifications: #{e.message}" end @@ -540,7 +540,7 @@ def email_entries(selected_folders, selected_files) def download_entries(selected_folders, selected_files) zip = Zip.new zip_entries(zip, selected_folders, selected_files) - zip.files.each do |f| + zip.dmsf_files.each do |f| # Action audit = DmsfFileRevisionAccess.new audit.user = User.current @@ -550,7 +550,7 @@ def download_entries(selected_folders, selected_files) end # Notifications begin - DmsfMailer.deliver_files_downloaded(@project, zip.files, request.remote_ip) + DmsfMailer.deliver_files_downloaded @project, zip.dmsf_files, request.remote_ip rescue StandardError => e Rails.logger.error "Could not send email notifications: #{e.message}" end @@ -570,7 +570,7 @@ def zip_entries(zip, selected_folders, selected_files) folder = DmsfFolder.visible.find_by(id: selected_folder_id) raise RedmineDmsf::Errors::DmsfFileNotFoundError unless folder - zip.add_folder folder, member, folder&.dmsf_folder&.dmsf_path_str + zip.add_dmsf_folder folder, member, folder&.dmsf_folder&.dmsf_path_str end selected_files.each do |selected_file_id| file = DmsfFile.visible.find_by(id: selected_file_id) @@ -581,10 +581,10 @@ def zip_entries(zip, selected_folders, selected_files) raise RedmineDmsf::Errors::DmsfAccessError end - zip.add_file file, member, file.dmsf_folder&.dmsf_path_str + zip.add_dmsf_file file, member, file.dmsf_folder&.dmsf_path_str end max_files = Setting.plugin_redmine_dmsf['dmsf_max_file_download'].to_i - raise RedmineDmsf::Errors::DmsfZipMaxFilesError if max_files.positive? && zip.files.length > max_files + raise RedmineDmsf::Errors::DmsfZipMaxFilesError if max_files.positive? && zip.dmsf_files.length > max_files zip end diff --git a/lib/redmine_dmsf/dmsf_zip.rb b/lib/redmine_dmsf/dmsf_zip.rb index ed76684a..ab26874e 100644 --- a/lib/redmine_dmsf/dmsf_zip.rb +++ b/lib/redmine_dmsf/dmsf_zip.rb @@ -24,15 +24,20 @@ module RedmineDmsf module DmsfZip # ZIP class Zip - attr_reader :files + attr_reader :dmsf_files def initialize @temp_file = Tempfile.new(%w[dmsf_zip_ .zip], Rails.root.join('tmp')) @zip_file = ::Zip::OutputStream.open(@temp_file) @files = [] + @dmsf_files = [] @folders = [] end + def read + File.read @temp_file + end + def finish @zip_file.close @temp_file.path @@ -42,37 +47,69 @@ def close @zip_file.close end - def add_file(file, member, root_path = nil) - return if @files.include?(file) - unless file&.last_revision && File.exist?(file.last_revision&.disk_file) + def add_dmsf_file(dmsf_file, member = nil, root_path = nil, path = nil) + unless dmsf_file&.last_revision && File.exist?(dmsf_file.last_revision.disk_file) raise RedmineDmsf::Errors::DmsfFileNotFoundError end - string_path = file.dmsf_folder.nil? ? '' : (file.dmsf_folder.dmsf_path_str + File::SEPARATOR) - string_path = string_path[(root_path.length + 1)..string_path.length] if root_path - string_path += file.formatted_name(member) + if path + string_path = path + else + string_path = dmsf_file.dmsf_folder.nil? ? '' : (dmsf_file.dmsf_folder.dmsf_path_str + File::SEPARATOR) + string_path = string_path[(root_path.length + 1)..string_path.length] if root_path + end + string_path += dmsf_file.formatted_name(member) + + return if @files.include?(string_path) + zip_entry = ::Zip::Entry.new(@zip_file, string_path, nil, nil, nil, nil, nil, nil, - ::Zip::DOSTime.at(file.last_revision.updated_at)) - @zip_file.put_next_entry(zip_entry) - File.open(file.last_revision.disk_file, 'rb') do |f| + ::Zip::DOSTime.at(dmsf_file.last_revision.updated_at)) + @zip_file.put_next_entry zip_entry + File.open(dmsf_file.last_revision.disk_file, 'rb') do |f| + while (buffer = f.read(8192)) + @zip_file.write buffer + end + end + @files << string_path + @dmsf_files << dmsf_file + end + + def add_attachment(attachment, path) + return if @files.include?(path) + + raise RedmineDmsf::Errors::DmsfFileNotFoundError unless File.exist?(attachment.diskfile) + + zip_entry = ::Zip::Entry.new(@zip_file, path, nil, nil, nil, nil, nil, nil, + ::Zip::DOSTime.at(attachment.created_on)) + @zip_file.put_next_entry zip_entry + File.open(attachment.diskfile, 'rb') do |f| while (buffer = f.read(8192)) - @zip_file.write(buffer) + @zip_file.write buffer end end - @files << file + @files << path end - def add_folder(folder, member, root_path = nil) - return if @folders.include?(folder) + def add_raw_file(filename, data) + return if @files.include?(filename) + + zip_entry = ::Zip::Entry.new(@zip_file, filename, nil, nil, nil, nil, nil, nil, ::Zip::DOSTime.now) + @zip_file.put_next_entry zip_entry + @zip_file.write data + @files << filename + end - string_path = folder.dmsf_path_str + File::SEPARATOR + def add_dmsf_folder(dmsf_folder, member, root_path = nil) + string_path = dmsf_folder.dmsf_path_str + File::SEPARATOR string_path = string_path[(root_path.length + 1)..string_path.length] if root_path zip_entry = ::Zip::Entry.new(@zip_file, string_path, nil, nil, nil, nil, nil, nil, - ::Zip::DOSTime.at(folder.modified)) - @zip_file.put_next_entry(zip_entry) - @folders << folder - folder.dmsf_folders.visible.each { |subfolder| add_folder(subfolder, member, root_path) } - folder.dmsf_files.visible.each { |file| add_file(file, member, root_path) } + ::Zip::DOSTime.at(dmsf_folder.modified)) + return if @folders.include?(string_path) + + @zip_file.put_next_entry zip_entry + @folders << string_path + dmsf_folder.dmsf_folders.visible.each { |folder| add_dmsf_folder(folder, member, root_path) } + dmsf_folder.dmsf_files.visible.each { |file| add_dmsf_file(file, member, root_path) } end end end diff --git a/test/helper_test.rb b/test/helper_test.rb index 966a5585..95405699 100644 --- a/test/helper_test.rb +++ b/test/helper_test.rb @@ -53,10 +53,9 @@ def setup [@project1, @project2].each do |prj| prj.enable_module! :dmsf end - Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.join('files', ['dmsf']) + Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.join('files', 'dmsf') Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = nil Setting.plugin_redmine_dmsf['dmsf_projects_as_subfolders'] = nil - Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.join('files', ['dmsf']) FileUtils.cp_r File.join(File.expand_path('../fixtures/files', __FILE__), '.'), DmsfFile.storage_path end diff --git a/test/unit/lib/redmine_dmsf/dmsf_macros_test.rb b/test/unit/lib/redmine_dmsf/dmsf_macros_test.rb index 28bf3873..cc3cc440 100644 --- a/test/unit/lib/redmine_dmsf/dmsf_macros_test.rb +++ b/test/unit/lib/redmine_dmsf/dmsf_macros_test.rb @@ -29,8 +29,7 @@ class DmsfMacrosTest < RedmineDmsf::Test::HelperTest include Rails.application.routes.url_helpers include ActionView::Helpers::UrlHelper - fixtures :dmsf_folders, :dmsf_files, :dmsf_file_revisions, :dmsf_folders, :dmsf_files, - :dmsf_file_revisions + fixtures :dmsf_folders, :dmsf_files, :dmsf_file_revisions def setup super diff --git a/test/unit/lib/redmine_dmsf/dmsf_zip_test.rb b/test/unit/lib/redmine_dmsf/dmsf_zip_test.rb new file mode 100644 index 00000000..85f0110f --- /dev/null +++ b/test/unit/lib/redmine_dmsf/dmsf_zip_test.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +# Redmine plugin for Document Management System "Features" +# +# Karel Pičman +# +# 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. + +require File.expand_path('../../../../test_helper', __FILE__) + +# Plugin tests +class DmsfZipTest < RedmineDmsf::Test::HelperTest + fixtures :dmsf_folders, :dmsf_files, :dmsf_file_revisions, :attachments + def setup + @zip = RedmineDmsf::DmsfZip::Zip.new + + Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.join('files', 'dmsf') + FileUtils.cp_r File.join(File.expand_path('../../../../fixtures/files', __FILE__), '.'), DmsfFile.storage_path + @dmsf_file1 = DmsfFile.find(1) + @dmsf_folder2 = DmsfFolder.find(2) + set_fixtures_attachments_directory + @attachment4 = Attachment.find(4) + end + + def teardown + # Delete our tmp folder + FileUtils.rm_rf DmsfFile.storage_path + rescue StandardError => e + Rails.logger.error e.message + end + + def test_add_dmsf_file + @zip.add_dmsf_file @dmsf_file1 + assert_equal 1, @zip.dmsf_files.size + zip_file = @zip.finish + Zip::File.open(zip_file) do |file| + file.each do |entry| + assert_equal @dmsf_file1.last_revision.name, entry.name + end + end + end + + def test_add_attachment + @zip.add_attachment @attachment4, @attachment4.filename + assert_equal 0, @zip.dmsf_files.size + zip_file = @zip.finish + Zip::File.open(zip_file) do |file| + file.each do |entry| + assert_equal @attachment4.filename, entry.name + end + end + end + + def test_add_raw_file + filename = 'data.txt' + content = '1,2,3' + @zip.add_raw_file filename, content + assert_equal 0, @zip.dmsf_files.size + zip_file = @zip.finish + Zip::File.open(zip_file) do |file| + file.each do |entry| + assert_equal filename, entry.name + assert_equal content, entry.get_input_stream.read + end + end + end + + def test_read + @zip.add_dmsf_file @dmsf_file1 + assert_not_empty @zip.read + end + + def test_finish + @zip.add_dmsf_file @dmsf_file1 + zip_file = @zip.finish + assert File.exist?(zip_file) + end + + def test_close + @zip.add_dmsf_file @dmsf_file1 + assert @zip.close + end +end