diff --git a/.rubocop.yml b/.rubocop.yml index 211a0725..cd75d4d2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -111,4 +111,3 @@ Style/OpenStructUse: Style/OptionalBooleanParameter: Exclude: - lib/redmine_dmsf/field_formats/dmsf_file_format.rb - \ No newline at end of file diff --git a/after_init.rb b/after_init.rb index df8e5a1b..7a9e5254 100644 --- a/after_init.rb +++ b/after_init.rb @@ -61,8 +61,8 @@ def dmsf_init Redmine::AccessControl.map do |map| map.project_module :dmsf do |pmap| - pmap.permission :view_dmsf_file_revision_accesses, read: true - pmap.permission :view_dmsf_file_revisions, read: true + pmap.permission :view_dmsf_file_revision_accesses, {}, read: true + pmap.permission :view_dmsf_file_revisions, {}, read: true pmap.permission :view_dmsf_folders, { dmsf: %i[show index] }, read: true pmap.permission :user_preferences, { dmsf_state: [:user_pref_save] }, require: :member pmap.permission(:view_dmsf_files, @@ -75,9 +75,8 @@ def dmsf_init { dmsf_public_urls: [:create] } pmap.permission :folder_manipulation, { dmsf: %i[new create delete edit save edit_root save_root lock unlock notify_activate - notify_deactivate restore drop], + notify_deactivate restore drop copymove], dmsf_folder_permissions: %i[new append autocomplete_for_user], - dmsf_folders_copy: %i[new copy move], dmsf_context_menus: [:dmsf] } pmap.permission :file_manipulation, { dmsf_files: %i[create_revision lock unlock delete_revision obsolete_revision @@ -85,7 +84,6 @@ def dmsf_init dmsf_upload: %i[upload_files upload commit_files commit delete_dmsf_attachment delete_dmsf_link_attachment multi_upload], dmsf_links: %i[new create destroy restore autocomplete_for_project autocomplete_for_folder], - dmsf_files_copy: %i[new copy move], dmsf_context_menus: [:dmsf] } pmap.permission :file_delete, { dmsf: %i[trash delete_entries empty_trash], @@ -97,8 +95,7 @@ def dmsf_init pmap.permission :manage_workflows, { dmsf_workflows: %i[index new create destroy show new_step add_step remove_step reorder_steps update update_step delete_step edit] } - pmap.permission :display_system_folders, - read: true + pmap.permission :display_system_folders, {}, read: true # Watchers pmap.permission :view_dmsf_file_watchers, {}, read: true pmap.permission :add_dmsf_file_watchers, { watchers: %i[new create append autocomplete_for_user] } diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index fc2dd48f..10b6fa22 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -35,6 +35,8 @@ class DmsfController < ApplicationController before_action :find_folder_by_title, only: [:show] before_action :query, only: %i[expand_folder show trash empty_trash index] before_action :project_roles, only: %i[new edit create save] + before_action :find_target_folder, only: %i[copymove entries_operation] + before_action :check_target_folder, only: [:entries_operation] accept_api_auth :show, :create, :save, :delete @@ -138,59 +140,55 @@ class DmsfController < ApplicationController flash[:error] = e.message end + def copymove + @ids = params[:ids] + member = Member.find_by(project_id: @project.id, user_id: User.current.id) + @fast_links = member&.dmsf_fast_links + unless @fast_links + @projects = DmsfFolder.allowed_target_projects_on_copy + @folders = DmsfFolder.directory_tree(@target_project, @folder) + @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) if params[:target_folder_id].present? + end + @back_url = params[:back_url] + render layout: !request.xhr? + end + def entries_operation # Download/Email - if params[:ids].present? - selected_folders = params[:ids].grep(/folder-\d+/).map { |x| Regexp.last_match(1).to_i if x =~ /folder-(\d+)/ } - selected_files = params[:ids].grep(/file-\d+/).map { |x| Regexp.last_match(1).to_i if x =~ /file-(\d+)/ } - selected_dir_links = params[:ids].grep(/folder-link-\d+/) - .map { |x| Regexp.last_match(1).to_i if x =~ /folder-link-(\d+)/ } - selected_file_links = params[:ids].grep(/file-link-\d+/) - .map { |x| Regexp.last_match(1).to_i if x =~ /file-link-(\d+)/ } - selected_url_links = params[:ids].grep(/url-link-\d+/) - .map { |x| Regexp.last_match(1).to_i if x =~ /url-link-(\d+)/ } - else - selected_folders = [] - selected_files = [] - selected_dir_links = [] - selected_file_links = [] - selected_url_links = [] - end - - if selected_folders.blank? && - selected_files.blank? && - selected_dir_links.blank? && - selected_file_links.blank? && - selected_url_links.blank? + if @selected_folders.blank? && @selected_files.blank? && @selected_links.blank? flash[:warning] = l(:warning_no_entries_selected) redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) return end - if selected_dir_links.present? && (params[:email_entries].present? || params[:download_entries].present?) - selected_folders = DmsfLink.where(id: selected_dir_links).pluck(:target_id) | selected_folders + if @selected_dir_links.present? && (params[:email_entries].present? || params[:download_entries].present?) + @selected_folders = DmsfLink.where(id: selected_dir_links).pluck(:target_id) | @selected_folders end - if selected_file_links.present? && (params[:email_entries].present? || params[:download_entries].present?) - selected_files = DmsfLink.where(id: selected_file_links).pluck(:target_id) | selected_files + if @selected_file_links.present? && (params[:email_entries].present? || params[:download_entries].present?) + @selected_files = DmsfLink.where(id: selected_file_links).pluck(:target_id) | @selected_files end begin if params[:email_entries].present? - email_entries selected_folders, selected_files + email_entries @selected_folders, @selected_files elsif params[:restore_entries].present? - restore_entries selected_folders, selected_files, selected_dir_links, selected_file_links, selected_url_links + restore_entries @selected_folders, @selected_files, @selected_links redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) elsif params[:delete_entries].present? - delete_entries(selected_folders, selected_files, selected_dir_links, selected_file_links, selected_url_links, - false) + delete_entries @selected_folders, @selected_files, @selected_links, false redirect_back_or_default dmsf_folder_path id: @project, folder_id: @folder elsif params[:destroy_entries].present? - delete_entries(selected_folders, selected_files, selected_dir_links, selected_file_links, selected_url_links, - true) + delete_entries @selected_folders, @selected_files, @selected_links, true + redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) + elsif params[:move_entries].present? + move_entries @selected_folders, @selected_files, @selected_links + redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) + elsif params[:copy_entries].present? + copy_entries @selected_folders, @selected_files, @selected_links redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) else - download_entries selected_folders, selected_files + download_entries @selected_folders, @selected_files end rescue RedmineDmsf::Errors::DmsfFileNotFoundError render_404 @@ -574,7 +572,7 @@ class DmsfController < ApplicationController zip end - def restore_entries(selected_folders, selected_files, selected_dir_links, selected_file_links, selected_url_links) + def restore_entries(selected_folders, selected_files, selected_links) # Folders selected_folders.each do |id| folder = DmsfFolder.find_by(id: id) @@ -589,7 +587,7 @@ class DmsfController < ApplicationController flash[:error] = file.errors.full_messages.to_sentence unless file.restore end # Links - (selected_dir_links + selected_file_links + selected_url_links).each do |id| + selected_links.each do |id| link = DmsfLink.find_by(id: id) raise RedmineDmsf::Errors::DmsfFileNotFoundError unless link @@ -597,8 +595,7 @@ class DmsfController < ApplicationController end end - def delete_entries(selected_folders, selected_files, selected_dir_links, selected_file_links, selected_url_links, - commit) + def delete_entries(selected_folders, selected_files, selected_links, commit) # Folders selected_folders.each do |id| raise RedmineDmsf::Errors::DmsfAccessError unless User.current.allowed_to?(:folder_manipulation, @project) @@ -613,9 +610,10 @@ class DmsfController < ApplicationController # Files deleted_files = [] not_deleted_files = [] - selected_files.each do |id| + if selected_files.any? raise RedmineDmsf::Errors::DmsfAccessError unless User.current.allowed_to?(:file_delete, @project) - + end + selected_files.each do |id| file = DmsfFile.find_by(id: id) if file if file.delete(commit: commit) @@ -647,21 +645,70 @@ class DmsfController < ApplicationController flash[:warning] = l(:warning_some_entries_were_not_deleted, entries: not_deleted_files.map(&:title).join(', ')) end # Links - selected_dir_links.each do |id| + if selected_links.any? raise RedmineDmsf::Errors::DmsfAccessError unless User.current.allowed_to?(:folder_manipulation, @project) - - link = DmsfLink.find_by(id: id) - link&.delete commit: commit end - (selected_file_links + selected_url_links).each do |id| - raise RedmineDmsf::Errors::DmsfAccessError unless User.current.allowed_to?(:file_delete, @project) - + selected_links.each do |id| link = DmsfLink.find_by(id: id) link&.delete commit: commit end flash[:notice] = l(:notice_entries_deleted) if flash[:error].blank? && flash[:warning].blank? end + def copy_entries(selected_folders, selected_files, selected_links) + # Folders + selected_folders.each do |id| + folder = DmsfFolder.find_by(id: id) + new_folder = folder.copy_to(@target_project, @target_folder) + raise(StandardError, new_folder.errors.full_messages.to_sentence) unless new_folder.errors.empty? + end + # Files + selected_files.each do |id| + file = DmsfFile.find_by(id: id) + new_file = file.copy_to(@target_project, @target_folder) + raise(StandardError, new_file.errors.full_messages.to_sentence) unless new_file.errors.empty? + end + # Links + selected_links.each do |id| + link = DmsfLink.find_by(id: id) + new_link = link.copy_to(@target_project, @target_folder) + raise(StandardError, new_link.errors.full_messages.to_sentence) unless new_link.errors.empty? + end + flash[:notice] = l(:notice_entries_copied) if flash[:error].blank? && flash[:warning].blank? + end + + def move_entries(selected_folders, selected_files, selected_links) + # Permissions + if selected_folders.any? && !User.current.allowed_to?(:folder_manipulation, @project) + raise RedmineDmsf::Errors::DmsfAccessError + end + if (selected_folders.any? || selected_links.any?) && !User.current.allowed_to?(:file_manipulation, @project) + raise RedmineDmsf::Errors::DmsfAccessError + end + # Folders + selected_folders.each do |id| + folder = DmsfFolder.find_by(id: id) + unless folder.move_to(@target_project, @target_folder) + raise(StandardError, folder.errors.full_messages.to_sentence) + end + end + # Files + selected_files.each do |id| + file = DmsfFile.find_by(id: id) + unless file.move_to(@target_project, @target_folder) + raise(StandardError, file.errors.full_messages.to_sentence) + end + end + # Links + selected_links.each do |id| + link = DmsfLink.find_by(id: id) + unless link.move_to(@target_project, @target_folder) + raise(StandardError, link.errors.full_messages.to_sentence) + end + end + flash[:notice] = l(:notice_entries_moved) if flash[:error].blank? && flash[:warning].blank? + end + def find_folder @folder = DmsfFolder.find(params[:folder_id]) if params[:folder_id].present? rescue ActiveRecord::RecordNotFound @@ -719,4 +766,68 @@ class DmsfController < ApplicationController @project_roles = Role.givable.joins(:member_roles).joins(:members).where(members: { project_id: @project.id }) .distinct end + + def find_target_folder + @target_project = if params[:dmsf_entries] && params[:dmsf_entries][:target_project_id].present? + Project.find params[:dmsf_entries][:target_project_id] + else + @project + end + if params[:dmsf_entries] && params[:dmsf_entries][:target_folder_id].present? + target_folder_id = params[:dmsf_entries][:target_folder_id] + @target_folder = DmsfFolder.find(target_folder_id) + raise ActiveRecord::RecordNotFound unless DmsfFolder.visible.exists?(id: target_folder_id) + + @target_project = @target_folder&.project + end + rescue ActiveRecord::RecordNotFound + render_404 + end + + def check_target_folder + if params[:ids].present? + @selected_folders = params[:ids].grep(/folder-\d+/).map { |x| Regexp.last_match(1).to_i if x =~ /folder-(\d+)/ } + @selected_files = params[:ids].grep(/file-\d+/).map { |x| Regexp.last_match(1).to_i if x =~ /file-(\d+)/ } + @selected_dir_links = params[:ids].grep(/folder-link-\d+/) + .map { |x| Regexp.last_match(1).to_i if x =~ /folder-link-(\d+)/ } + @selected_file_links = params[:ids].grep(/file-link-\d+/) + .map { |x| Regexp.last_match(1).to_i if x =~ /file-link-(\d+)/ } + @selected_url_links = params[:ids].grep(/url-link-\d+/) + .map { |x| Regexp.last_match(1).to_i if x =~ /url-link-(\d+)/ } + @selected_links = @selected_dir_links + @selected_file_links + @selected_url_links + else + @selected_folders = [] + @selected_files = [] + @selected_links = [] + end + if params[:copy_entries].present? || params[:move_entries].present? + begin + # Prevent copying/moving to the same destination + folders = DmsfFolder.where(id: @selected_folders).to_a + files = DmsfFile.where(id: @selected_files).to_a + links = DmsfLink.where(id: @selected_links).to_a + (folders + files + links).each do |entry| + raise RedmineDmsf::Errors::DmsfParentError if entry.dmsf_folder == @target_folder || entry == @target_folder + end + # Prevent recursion + if params[:move_entries].present? + folders.each do |entry| + b = entry.any_child?(@target_folder) + raise RedmineDmsf::Errors::DmsfParentError if entry.any_child?(@target_folder) + end + end + # Check permissions + if (@target_folder && (@target_folder.locked_for_user? || + !DmsfFolder.permissions?(@target_folder, allow_system: false))) || + !@target_project.allows_to?(:folder_manipulation) + raise RedmineDmsf::Errors::DmsfAccessError + end + rescue RedmineDmsf::Errors::DmsfParentError + flash[:error] = l(:error_target_folder_same) + redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) + rescue RedmineDmsf::Errors::DmsfAccessError + render_403 + end + end + end end diff --git a/app/controllers/dmsf_files_copy_controller.rb b/app/controllers/dmsf_files_copy_controller.rb deleted file mode 100644 index bdc32c07..00000000 --- a/app/controllers/dmsf_files_copy_controller.rb +++ /dev/null @@ -1,138 +0,0 @@ -# frozen_string_literal: true - -# Redmine plugin for Document Management System "Features" -# -# Copyright © 2011 Vít Jonáš -# Copyright © 2011-23 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. - -# Files copy controller -class DmsfFilesCopyController < ApplicationController - menu_item :dmsf - - before_action :find_file - before_action :authorize - before_action :find_target_folder - before_action :check_target_folder, only: %i[copy move] - - accept_api_auth :copy, :move - - helper :dmsf - - def new - member = Member.find_by(project_id: @project.id, user_id: User.current.id) - @fast_links = member&.dmsf_fast_links - unless @fast_links - @projects = DmsfFile.allowed_target_projects_on_copy - @folders = DmsfFolder.directory_tree(@target_project, @folder) - end - @back_url = params[:back_url] - render layout: !request.xhr? - end - - def copy - new_file = @file.copy_to(@target_project, @target_folder) - failure = new_file.nil? || new_file.errors.present? - if failure - flash[:error] = new_file ? new_file.errors.full_messages.to_sentence : @file.errors.full_messages.to_sentence - else - flash[:notice] = l(:notice_successful_update) - end - respond_to do |format| - format.html do - redirect_back_or_default dmsf_folder_path(id: @file.project, folder_id: @file.dmsf_folder) - end - format.api do - if failure - render_validation_errors new_file || @file - else - render_api_ok - end - end - end - end - - def move - success = @file.move_to(@target_project, @target_folder) - if success - flash[:notice] = l(:notice_successful_update) - else - flash[:error] = @file.errors.full_messages.to_sentence - end - respond_to do |format| - format.html do - redirect_back_or_default dmsf_folder_path(id: @file.project, folder_id: @file.dmsf_folder) - end - format.api do - if success - render_api_ok - else - render_validation_errors @file - end - end - end - end - - private - - def find_file - raise ActiveRecord::RecordNotFound unless DmsfFile.exists?(id: params[:id]) - - @file = DmsfFile.visible.find params[:id] - raise RedmineDmsf::Errors::DmsfAccessError if @file.locked_for_user? - - @project = @file.project - rescue ActiveRecord::RecordNotFound - render_404 - rescue RedmineDmsf::Errors::DmsfAccessError - render_403 - end - - def find_target_folder - target_folder_id = if params[:target_folder_id].present? - params[:target_folder_id] - elsif params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_folder_id].present? - params[:dmsf_file_or_folder][:target_folder_id] - end - @target_folder = DmsfFolder.visible.find(target_folder_id) if target_folder_id - target_project_id = if params[:target_project_id].present? - params[:target_project_id] - elsif params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_project_id].present? - params[:dmsf_file_or_folder][:target_project_id] - else - @target_folder&.project_id - end - @target_project = target_project_id ? Project.visible.find(target_project_id) : @project - rescue ActiveRecord::RecordNotFound - render_404 - end - - def check_target_folder - if (@target_folder && @target_folder == @file.dmsf_folder) || - (@target_folder.nil? && @file.dmsf_folder.nil? && @target_project == @file.project) - flash[:error] = l(:error_target_folder_same) - redirect_to action: :new, id: @file, target_project_id: @target_project&.id, target_folder_id: @target_folder - return - end - if (@target_folder && (@target_folder.locked_for_user? || - !DmsfFolder.permissions?(@target_folder, allow_system: false))) || - !@target_project.allows_to?(:file_manipulation) - raise RedmineDmsf::Errors::DmsfAccessError - end - rescue RedmineDmsf::Errors::DmsfAccessError - render_403 - end -end diff --git a/app/controllers/dmsf_folders_copy_controller.rb b/app/controllers/dmsf_folders_copy_controller.rb deleted file mode 100644 index 80f727d5..00000000 --- a/app/controllers/dmsf_folders_copy_controller.rb +++ /dev/null @@ -1,137 +0,0 @@ -# frozen_string_literal: true - -# Redmine plugin for Document Management System "Features" -# -# Copyright © 2011 Vít Jonáš -# Copyright © 2011-23 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. - -# Folders copy controller -class DmsfFoldersCopyController < ApplicationController - menu_item :dmsf - - before_action :find_folder - before_action :authorize - before_action :find_target_folder - before_action :check_target_folder, only: %i[copy move] - - accept_api_auth :copy, :move - - helper :dmsf - - def new - member = Member.find_by(project_id: @project.id, user_id: User.current.id) - @fast_links = member&.dmsf_fast_links - unless @fast_links - @projects = DmsfFolder.allowed_target_projects_on_copy - @folders = DmsfFolder.directory_tree(@target_project, @folder) - @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) if params[:target_folder_id].present? - end - @back_url = params[:back_url] - render layout: !request.xhr? - end - - def copy - new_folder = @folder.copy_to(@target_project, @target_folder) - success = new_folder.errors.empty? - if success - flash[:notice] = l(:notice_successful_update) - else - flash[:error] = new_folder.errors.full_messages.to_sentence - end - respond_to do |format| - format.html do - redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder.dmsf_folder) - end - format.api do - if success - render_api_ok - else - render_validation_errors new_folder - end - end - end - end - - def move - success = @folder.move_to(@target_project, @target_folder) - if success - flash[:notice] = l(:notice_successful_update) - else - flash[:error] = @folder.errors.full_messages.to_sentence - end - respond_to do |format| - format.html do - redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder.dmsf_folder) - end - format.api do - if success - render_api_ok - else - render_validation_errors @folder - end - end - end - end - - private - - def find_folder - raise ActiveRecord::RecordNotFound unless DmsfFolder.exists?(id: params[:id]) - - @folder = DmsfFolder.visible.find params[:id] - raise RedmineDmsf::Errors::DmsfAccessError if @folder.locked_for_user? - - @project = @folder.project - rescue ActiveRecord::RecordNotFound - render_404 - rescue RedmineDmsf::Errors::DmsfAccessError - render_403 - end - - def find_target_folder - @target_project = if params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_project_id].present? - Project.find params[:dmsf_file_or_folder][:target_project_id] - else - @project - end - if params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_folder_id].present? - target_folder_id = params[:dmsf_file_or_folder][:target_folder_id] - @target_folder = DmsfFolder.find(target_folder_id) - raise ActiveRecord::RecordNotFound unless DmsfFolder.visible.exists?(id: target_folder_id) - - @target_project = @target_folder&.project - end - rescue ActiveRecord::RecordNotFound - render_404 - end - - def check_target_folder - if (@target_folder && @target_folder == @folder.dmsf_folder) || - (@target_folder.nil? && @folder.dmsf_folder.nil? && @target_project == @folder.project) - flash[:error] = l(:error_target_folder_same) - redirect_to action: :new, id: @folder, target_project_id: @target_project.id, target_folder_id: @target_folder - return - end - if (@target_folder && (@target_folder.locked_for_user? || - !DmsfFolder.permissions?(@target_folder, allow_system: false))) || - !@target_project.allows_to?(:folder_manipulation) - raise RedmineDmsf::Errors::DmsfAccessError - end - rescue RedmineDmsf::Errors::DmsfAccessError - render_403 - end -end diff --git a/app/models/dmsf_folder.rb b/app/models/dmsf_folder.rb index 9c55befc..7482df3d 100644 --- a/app/models/dmsf_folder.rb +++ b/app/models/dmsf_folder.rb @@ -304,12 +304,11 @@ class DmsfFolder < ApplicationRecord new_folder.description = description new_folder.user = User.current new_folder.custom_values = [] - custom_values.each do |cv| - v = CustomValue.new - v.custom_field = cv.custom_field - v.value = cv.value - new_folder.custom_values << v - end + new_folder.custom_field_values = + custom_field_values.inject({}) do |h, v| + h[v.custom_field_id] = v.value + h + end unless new_folder.save Rails.logger.error new_folder.errors.full_messages.to_sentence return new_folder @@ -601,6 +600,15 @@ class DmsfFolder < ApplicationRecord title end + # Check whether any child folder is equal to the folder + def any_child?(folder) + dmsf_folders.each do |child| + return true if child == folder + child.any_child? folder + end + false + end + class << self def directory_subtree(tree, folder, level, current_folder) folders = folder.dmsf_folders.visible.to_a diff --git a/app/views/dmsf/copymove.html.erb b/app/views/dmsf/copymove.html.erb new file mode 100644 index 00000000..7bc43864 --- /dev/null +++ b/app/views/dmsf/copymove.html.erb @@ -0,0 +1,75 @@ +<% + # Redmine plugin for Document Management System "Features" + # + # Copyright © 2011 Vít Jonáš + # Copyright © 2012 Daniel Munn + # Copyright © 2011-23 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. +%> + +<%= render partial: '/dmsf/path', locals: { folder: nil, filename: nil, title: nil } %> + +<% if @projects.present? || @fast_links %> + <%= form_tag(entries_operations_dmsf_path, id: 'copyForm') do %> + <% @ids.each do |id| %> + <%= hidden_field_tag 'ids[]', id %> + <% end %> + <%= hidden_field_tag 'back_url', @back_url %> + <%= hidden_field_tag 'id', @project %> + <%= hidden_field_tag 'copy_entries', true %> +
+ <% unless @fast_links %> +

+ <%= label_tag 'dmsf_entries[target_project_id]', l(:field_target_project) %> + <%= select_tag 'dmsf_entries[target_project_id]', + project_tree_options_for_select(@projects, selected: @target_project) %> +

+ <% end %> +

+ <%= label_tag 'dmsf_entries[target_folder_id]', l(:field_target_folder) %><%= ' #' if @fast_links %> + <% if @fast_links %> + <%= text_field_tag 'dmsf_entries[target_folder_id]', '', required: true, max_length: 255 %> + <% else %> + <%= select_tag 'dmsf_entries[target_folder_id]', options_for_select(@folders, selected: @target_folder&.id) %> + <% end %> +

+
+

+ <%= submit_tag l(:button_copy), id: 'copy_button' %> + <%# TODO: Lock and proper permissions %> + <% if User.current.allowed_to?(:folder_manipulation, @project) && + User.current.allowed_to?(:file_manipulation, @project)%> + <%= submit_tag l(:button_move), id: 'move_button' %> + <% end %> +

+ <% end %> +<% end %> + +<%= late_javascript_tag do %> + $('#move_button').click(function(event) { + $('#copyForm').attr('action', "<%= entries_operations_dmsf_path(ids: @ids, move_entries: true) %>"); + $('#copyForm').submit(); + }); + <% unless @fast_links %> + $('#dmsf_entries_target_project_id').change(function () { + $('#content').load("<%= copymove_entries_path(id: @project, folder_id: @folder, ids: @ids) %>", + $('#copyForm').serialize()); + }); + $('#dmsf_entries_target_project_id').select2(); + $('#dmsf_entries_target_folder_id').select2(); + <% end %> +<% end %> + diff --git a/app/views/dmsf_context_menus/_file.html.erb b/app/views/dmsf_context_menus/_file.html.erb index 3f995ead..82503ebe 100644 --- a/app/views/dmsf_context_menus/_file.html.erb +++ b/app/views/dmsf_context_menus/_file.html.erb @@ -24,8 +24,8 @@ <% unless dmsf_link %>
  • - <%= link_to "#{l(:button_copy)}/#{l(:button_move)}", copy_file_path(id: dmsf_file, back_url: back_url), - title: l(:title_copy), class: 'icon icon-copy' %> + <%= context_menu_link "#{l(:button_copy)}/#{l(:button_move)}", copymove_entries_path(id: project, folder_id: folder, + ids: ["file-#{dmsf_file.id}"], back_url: back_url), class: 'icon icon-copy' %>
  • <%= link_to l(:label_link_to), diff --git a/app/views/dmsf_context_menus/_folder.html.erb b/app/views/dmsf_context_menus/_folder.html.erb index 5f0ac539..147d72bf 100644 --- a/app/views/dmsf_context_menus/_folder.html.erb +++ b/app/views/dmsf_context_menus/_folder.html.erb @@ -26,8 +26,8 @@ <% end %> <% unless dmsf_link %>
  • - <%= context_menu_link "#{l(:button_copy)}/#{l(:button_move)}", copy_folder_path(id: dmsf_folder, back_url: back_url), - class: 'icon icon-copy', disabled: !allowed || locked %> + <%= context_menu_link "#{l(:button_copy)}/#{l(:button_move)}", copymove_entries_path(id: project, folder_id: folder, + ids: ["folder-#{dmsf_folder.id}"], back_url: back_url), class: 'icon icon-copy', disabled: !allowed || locked %>
  • <%= context_menu_link l(:label_link_to), diff --git a/app/views/dmsf_context_menus/_multiple.html.erb b/app/views/dmsf_context_menus/_multiple.html.erb index ed5d1e19..5df21dc4 100644 --- a/app/views/dmsf_context_menus/_multiple.html.erb +++ b/app/views/dmsf_context_menus/_multiple.html.erb @@ -27,9 +27,12 @@ ids: params[:ids], email_entries: true), method: :post, class: 'icon icon-email', disabled: !email_allowed %>
  • +
  • + <%= context_menu_link "#{l(:button_copy)}/#{l(:button_move)}", copymove_entries_path(id: project, folder_id: folder, + ids: params[:ids], back_url: back_url), class: 'icon icon-copy', disabled: project.nil? %> +
  • <%= context_menu_link l(:button_delete), entries_operations_dmsf_path(id: project, folder_id: folder, ids: params[:ids], delete_entries: true), method: :post, class: 'icon icon-del', - data: { confirm: l(:text_are_you_sure) }, id: 'dmsf-cm-delete', - disabled: !allowed %> + data: { confirm: l(:text_are_you_sure) }, id: 'dmsf-cm-delete', disabled: !allowed %>
  • diff --git a/app/views/dmsf_context_menus/_revisions.html.erb b/app/views/dmsf_context_menus/_revisions.html.erb index 67f131c1..ee898370 100644 --- a/app/views/dmsf_context_menus/_revisions.html.erb +++ b/app/views/dmsf_context_menus/_revisions.html.erb @@ -36,8 +36,8 @@ dmsf_file_id: file.id, type: 'link_to', back_url: back_url), title: l(:title_create_link), class: 'icon dmsf-icon-link' %> -<%= link_to "#{l(:button_copy)}/#{l(:button_move)}", copy_file_path(id: file, back_url: back_url), - title: l(:title_copy), class: 'icon icon-copy' %> +<%= context_menu_link "#{l(:button_copy)}/#{l(:button_move)}", copymove_entries_path(id: project, + folder_id: file.dmsf_folder, ids: ["file-#{file.id}"], back_url: back_url), class: 'icon icon-copy' %> <% member = Member.find_by(user_id: User.current.id, project_id: file.project.id) %> <% filename = file.last_revision&.formatted_name(member) %> <%= link_to l(:button_download), static_dmsf_file_path(file, filename: filename), class: 'icon icon-download', diff --git a/app/views/dmsf_files_copy/new.html.erb b/app/views/dmsf_files_copy/new.html.erb deleted file mode 100644 index 7d6793ba..00000000 --- a/app/views/dmsf_files_copy/new.html.erb +++ /dev/null @@ -1,35 +0,0 @@ -<% - # Redmine plugin for Document Management System "Features" - # - # Copyright © 2011 Vít Jonáš - # Copyright © 2012 Daniel Munn - # Copyright © 2011-23 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. -%> - -<%= render partial: '/dmsf/path', locals: { folder: @file.dmsf_folder, filename: @file.title, title: nil } %> - -<%= render partial: '/dmsf_folders_copy/form', - locals: { - projects: @projects, - project: @project, - target_project: @target_project, - folders: @folders, - file_or_folder: @file, - target_folder: @target_folder, - permission: :file_manipulation, - fast_links: @fast_links, - back_url: @back_url } %> diff --git a/app/views/dmsf_folders_copy/_form.html.erb b/app/views/dmsf_folders_copy/_form.html.erb deleted file mode 100644 index a0cf6a87..00000000 --- a/app/views/dmsf_folders_copy/_form.html.erb +++ /dev/null @@ -1,63 +0,0 @@ -<% - # Redmine plugin for Document Management System "Features" - # - # Copyright © 2011-23 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. -%> - -<% if projects.present? || fast_links %> - <%= form_tag({ action: 'copy', id: file_or_folder}, id: 'copyForm') do %> - <%= hidden_field_tag 'back_url', back_url %> -
    - <% unless fast_links %> -

    - <%= label_tag 'dmsf_file_or_folder[target_project_id]', l(:field_target_project) %> - <%= select_tag 'dmsf_file_or_folder[target_project_id]', - project_tree_options_for_select(projects, selected: target_project) %> -

    - <% end %> -

    - <%= label_tag 'dmsf_file_or_folder[target_folder_id]', l(:field_target_folder) %><%= ' #' if fast_links %> - <% if fast_links %> - <%= text_field_tag 'dmsf_file_or_folder[target_folder_id]', '', required: true, max_length: 255 %> - <% else %> - <%= select_tag 'dmsf_file_or_folder[target_folder_id]', - options_for_select(folders, selected: (target_folder.id if target_folder)) %> - <% end %> -

    -
    -

    - <%= submit_tag l(:button_copy), id: 'copy_button' %> - <% if !file_or_folder.locked? && User.current.allowed_to?(permission, project) %> - <%= submit_tag l(:button_move), id: 'move_button' %> - <% end %> -

    - <% end %> -<% end %> - -<%= late_javascript_tag do %> - $('#move_button').click(function(event) { - $('#copyForm').attr('action', "<%= url_for(action: 'move', id: file_or_folder) %>"); - $('#copyForm').submit(); - }); - <% unless fast_links %> - $('#dmsf_file_or_folder_target_project_id').change(function () { - $('#content').load("<%= url_for(action: 'new') %>", $('#copyForm').serialize()); - }); - $('#dmsf_file_or_folder_target_project_id').select2(); - $('#dmsf_file_or_folder_target_folder_id').select2(); - <% end %> -<% end %> diff --git a/app/views/dmsf_folders_copy/new.html.erb b/app/views/dmsf_folders_copy/new.html.erb deleted file mode 100644 index 96f96247..00000000 --- a/app/views/dmsf_folders_copy/new.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -<% - # Redmine plugin for Document Management System "Features" - # - # Copyright © 2011 Vít Jonáš - # Copyright © 2012 Daniel Munn - # Copyright © 2011-23 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. -%> - -<%= render partial: '/dmsf/path', locals: { folder: @folder, filename: nil, title: nil } %> - -<%= render partial: '/dmsf_folders_copy/form', locals: { - projects: @projects, - project: @project, - target_project: @target_project, - folders: @folders, - file_or_folder: @folder, - target_folder: @target_folder, - permission: :folder_manipulation, - fast_links: @fast_links, - back_url: @back_url} %> diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 62d9a856..a5a7310c 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -473,6 +473,9 @@ cs: text_fulltext_search: 'Full-textové vyhledávání v dokumentech vyžaduje přítomnost %{cmd1} and %{cmd2} na serveru.' + notice_entries_copied: Kopírování se podařilo + notice_entries_moved: Přesun se podařil + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/de.yml b/config/locales/de.yml index 800eb7a6..9e79ca7b 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -469,6 +469,9 @@ de: text_fulltext_search: 'Full-text Suche in Dokumente fordert die Existenz %{cmd1} and %{cmd2} auf dem Server.' + notice_entries_copied: Kopieren ist gelungen + notice_entries_moved: Verschiebung ist gelungen + easy_pages: modules: dmsf_locked_documents: Von mir gesperrte Dokumente diff --git a/config/locales/en.yml b/config/locales/en.yml index 76e1c78c..ad788c85 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -473,6 +473,9 @@ en: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/es.yml b/config/locales/es.yml index b43723d7..67aec635 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -473,6 +473,9 @@ es: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 12a79fe4..71708f99 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -452,6 +452,9 @@ fa: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: اسناد قفل شده‌ی من diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 1f1ef030..f08186fa 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -473,6 +473,9 @@ fr: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 5f0c1052..1ead73f3 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -472,6 +472,9 @@ hu: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/it.yml b/config/locales/it.yml index 49a054da..bdba88e0 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -473,6 +473,9 @@ it: # Italian strings thx 2 Matteo Arceci! text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 334f3a93..78369a21 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -474,6 +474,9 @@ ja: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: 自分がロック中の文書 diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 2e6b95f5..2dec219c 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -473,6 +473,9 @@ ko: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: 내 잠긴 파일 diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 79bdb7dc..020f1a51 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -473,6 +473,9 @@ nl: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 28d9888d..01e03d12 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -472,6 +472,9 @@ pl: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 8cc922f1..fb2fb6ed 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -473,6 +473,9 @@ pt-BR: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 9f7baf7e..88da994b 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -473,6 +473,9 @@ sl: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/ua.yml b/config/locales/ua.yml index cfad3284..7b4ca00f 100644 --- a/config/locales/ua.yml +++ b/config/locales/ua.yml @@ -475,6 +475,9 @@ uk: text_fulltext_search: 'Повнотекстовий пошук вимагає наявності %{cmd1} та %{cmd2} команд на сервері.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: Мої заблоковані документи diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index d7b67cfd..2d4f1d3e 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -472,6 +472,9 @@ zh-TW: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/locales/zh.yml b/config/locales/zh.yml index aad5a3a5..c38994e7 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -473,6 +473,9 @@ zh: text_fulltext_search: 'Full-text search in documents requires presence of %{cmd1} and %{cmd2} commands on the server.' + notice_entries_copied: Copying has succeeded + notice_entries_moved: Moving has succeeded + easy_pages: modules: dmsf_locked_documents: My locked documents diff --git a/config/routes.rb b/config/routes.rb index c4dead85..8b709ff3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +42,7 @@ if Redmine::Plugin.installed? 'redmine_dmsf' get '/projects/:id/dmsf/entries/download_email_entries', controller: 'dmsf', action: 'download_email_entries', as: 'download_email_entries' + get '/projects/:id/entries/copymove', to: 'dmsf#copymove', as: 'copymove_entries' get '/projects/:id/dmsf/lock', controller: 'dmsf', action: 'lock', as: 'lock_dmsf' get '/projects/:id/dmsf/unlock', controller: 'dmsf', action: 'unlock', as: 'unlock_dmsf' get '/projects/:id/dmsf/', controller: 'dmsf', action: 'show', as: 'dmsf_folder' diff --git a/lib/redmine_dmsf/errors/dmsf_parent_error.rb b/lib/redmine_dmsf/errors/dmsf_parent_error.rb new file mode 100644 index 00000000..605cc740 --- /dev/null +++ b/lib/redmine_dmsf/errors/dmsf_parent_error.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Redmine plugin for Document Management System "Features" +# +# Copyright © 2011 Vít Jonáš +# Copyright © 2011-23 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. + +module RedmineDmsf + module Errors + # Parent error + class DmsfParentError < StandardError + # Nothing to do + end + end +end diff --git a/test/functional/dmsf_controller_test.rb b/test/functional/dmsf_controller_test.rb index bc1952f4..64b6b661 100644 --- a/test/functional/dmsf_controller_test.rb +++ b/test/functional/dmsf_controller_test.rb @@ -454,4 +454,138 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase get :index assert_response :forbidden end + + def test_copymove_authorize_admin + @request.session[:user_id] = @admin.id + get :copymove, params: { id: @file1.project, folder_id: @file1.dmsf_folder, ids: ["file-#{@file1.id}"] } + assert_response :success + assert_template 'copymove' + end + + def test_copymove_authorize_non_member + @request.session[:user_id] = @someone.id + get :copymove, params: { id: @file1.project, folder_id: @file1.dmsf_folder, ids: ["file-#{@file1.id}"] } + assert_response :forbidden + end + + def test_copymove_authorize_member_no_module + @file1.project.disable_module! :dmsf + get :copymove, params: { id: @file1.project, folder_id: @file1.dmsf_folder, ids: ["file-#{@file1.id}"] } + assert_response :forbidden + end + + def test_copymove_authorize_forbidden + @role_manager.remove_permission! :folder_manipulation + get :copymove, params: { id: @file1.project, folder_id: @file1.dmsf_folder, ids: ["file-#{@file1.id}"] } + assert_response :forbidden + end + + def test_copymove_target_folder + get :copymove, params: { id: @file1.project, folder_id: @file1.dmsf_folder, ids: ["file-#{@file1.id}"] } + assert_response :success + assert_template 'copymove' + end + + def test_entries_copy + post :entries_operation, + params: { id: @file1.project, + dmsf_entries: { target_project_id: @folder1.project.id, target_folder_id: @folder1.id }, + ids: ["file-#{@file1.id}"], + copy_entries: true } + assert_response :redirect + assert_nil flash[:error] + end + + def test_entries_copy + post :entries_operation, + params: { id: @file1.project, + dmsf_entries: { target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder }, + ids: ["file-#{@file1.id}"], + copy_entries: true } + assert_response :redirect + assert_equal flash[:error], l(:error_target_folder_same) + end + + def test_entries_move_recursion + # Move a folder under the same folder + post :entries_operation, + params: { id: @folder1.project, + dmsf_entries: { target_project_id: @folder2.project.id, target_folder_id: @folder2.id }, + ids: ["folder-#{@folder1.id}"], + move_entries: true } + assert_response :redirect + assert_equal flash[:error], l(:error_target_folder_same) + end + + def test_entries_move_infinity + # Move the folder to itself + post :entries_operation, + params: { id: @folder1.project, + dmsf_entries: { target_project_id: @folder2.project.id, target_folder_id: @folder2.id }, + ids: ["folder-#{@folder2.id}"], + move_entries: true } + assert_response :redirect + assert_equal flash[:error], l(:error_target_folder_same) + end + + def test_entries_copy_to_locked_folder + @request.session[:user_id] = @admin.id + post :entries_operation, + params: { id: @folder1.project, + dmsf_entries: { target_project_id: @folder2.project.id, target_folder_id: @folder2.id }, + ids: ["file-#{@file1.id}"], + move_entries: true } + assert_response :forbidden + end + + def test_entries_copy_to_dmsf_not_enabled + @project2.disable_module! :dmsf + post :entries_operation, + params: { id: @project2.id, + dmsf_entries: { target_project_id: @project2.id }, + ids: ["file-#{@file1.id}"], + copy_entries: true } + assert_response :forbidden + end + + def test_entries_copy_to_dmsf_enabled + post :entries_operation, + params: { id: @project2.id, + dmsf_entries: { target_project_id: @project2.id }, + ids: ["file-#{@file1.id}"], + copy_entries: true } + assert_response :redirect + end + + def test_entries_copy_to_as_non_member + @request.session[:user_id] = @someone.id + post :entries_operation, + params: { id: @project2.id, + dmsf_entries: { target_project_id: @project2.id }, + ids: ["file-#{@file1.id}"], + copy_entries: true } + assert_response :forbidden + end + + def test_copymove_new_fast_links_enabled + member = Member.find_by(user_id: @jsmith.id, project_id: @project1.id) + assert member + member.dmsf_fast_links = true + member.save + get :copymove, params: { id: @file1.project, folder_id: @file1.dmsf_folder, ids: ["file-#{@file4.id}"] } + assert_response :success + assert_select 'label', { count: 0, text: l(:label_target_project) } + assert_select 'label', { count: 0, text: "#{l(:label_target_folder)}#" } + end + + def test_entries_move_fast_links_enabled + # Target project is not given + post :entries_operation, + params: { id: @project1.id, + dmsf_entries: { target_folder_id: @folder1.id }, + ids: ["file-#{@file1.id}"], + move_entries: true } + assert_response :redirect + assert_nil flash[:error] + end end diff --git a/test/functional/dmsf_files_controller_test.rb b/test/functional/dmsf_files_controller_test.rb deleted file mode 100644 index c806e287..00000000 --- a/test/functional/dmsf_files_controller_test.rb +++ /dev/null @@ -1,142 +0,0 @@ -# frozen_string_literal: true - -# Redmine plugin for Document Management System "Features" -# -# Copyright © 2011-23 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__) - -# Files controller -class DmsfFilesControllerTest < RedmineDmsf::Test::TestCase - fixtures :dmsf_folders, :dmsf_files, :dmsf_file_revisions, :dmsf_locks - - def setup - super - @request.session[:user_id] = @jsmith.id - end - - def teardown - super - DmsfFile.clear_previews - end - - def test_show_file_ok - # Permissions OK - get :show, params: { id: @file1.id } - assert_response :success - end - - def test_show_formatting_html - Setting.text_formatting = 'HTML' - get :show, params: { id: @file1.id } - assert_response :success - assert_include 'dmsf-description', response.body, 'dmsf-description class not found' - assert_include 'wiki-edit', response.body, 'wiki-edit class not found' - end - - def test_show_formatting_textile - Setting.text_formatting = 'Textile' - get :show, params: { id: @file1.id } - assert_response :success - assert_include 'dmsf-description', response.body, 'dmsf-description class not found' - assert_include 'wiki-edit', response.body, 'wiki-edit class not found' - end - - def test_show_file_forbidden - # Missing permissions - @role_manager.remove_permission! :view_dmsf_files - get :show, params: { id: @file1.id } - assert_response :forbidden - end - - def test_view_file_ok - # Permissions OK - get :view, params: { id: @file1.id } - assert_response :success - end - - def test_view_file_forbidden - # Missing permissions - @role_manager.remove_permission! :view_dmsf_files - get :view, params: { id: @file1.id } - assert_response :forbidden - end - - def test_view_preview - return unless RedmineDmsf::Preview.office_available? - - get :view, params: { id: @file13.id } - assert_response :success - assert_equal 'application/pdf', @response.media_type - assert @response.body.starts_with?('%PDF') - end - - def delete_forbidden - # Missing permissions - @role_manager.remove_permission! :file_manipulation - delete :delete, params: { id: @file1, folder_id: @file1.dmsf_folder, commit: false } - assert_response :forbidden - end - - def delete_locked - # Permissions OK but the file is locked - delete :delete, params: { id: @file2, folder_id: @file2.dmsf_folder, commit: false } - assert_response :redirect - assert_include l(:error_file_is_locked), flash[:error] - end - - def delete_ok - # Permissions OK and not locked - delete :delete, params: { id: @file1, folder_id: @file1.dmsf_folder, commit: false } - assert_redirected_to dmsf_folder_path(id: @file1.project, folder_id: @file1.dmsf_folder) - end - - def test_delete_in_subfolder - delete :delete, params: { id: @file4, folder_id: @file4.dmsf_folder, commit: false } - assert_redirected_to dmsf_folder_path(id: @file4.project, folder_id: @file4.dmsf_folder) - end - - def test_obsolete_revision_ok - get :obsolete_revision, params: { id: @file1.last_revision.id } - assert_redirected_to action: 'show', id: @file1 - end - - def test_obsolete_revision_missing_permissions - @role_manager.remove_permission! :file_manipulation - get :obsolete_revision, params: { id: @file1.last_revision.id } - assert :forbiden - end - - def test_create_revision - assert_difference 'DmsfFileRevision.count', +1 do - post :create_revision, - params: { - id: @file1.id, - version_major: @file1.last_revision.major_version, - version_minor: @file1.last_revision.minor_version + 1, - dmsf_file_revision: { - title: @file1.last_revision.title, - name: @file1.last_revision.name, - description: @file1.last_revision.description, - comment: 'New revision' - } - } - end - assert_redirected_to dmsf_folder_path(id: @file1.project) - assert_not_nil @file1.last_revision.digest - end -end diff --git a/test/functional/dmsf_folders_copy_controller_test.rb b/test/functional/dmsf_folders_copy_controller_test.rb deleted file mode 100644 index 3cb23ce1..00000000 --- a/test/functional/dmsf_folders_copy_controller_test.rb +++ /dev/null @@ -1,199 +0,0 @@ -# frozen_string_literal: true - -# Redmine plugin for Document Management System "Features" -# -# Copyright © 2011-23 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__) - -# Folders copy controller -class DmsfFoldersCopyControllerTest < RedmineDmsf::Test::TestCase - include Redmine::I18n - - fixtures :dmsf_locks, :dmsf_folders, :dmsf_files, :dmsf_file_revisions - - def setup - super - @request.session[:user_id] = @jsmith.id - end - - def test_authorize_admin - @request.session[:user_id] = @admin.id - get :new, params: { id: @folder1.id } - assert_response :success - assert_template 'new' - end - - def test_authorize_non_member - @request.session[:user_id] = @someone.id - get :new, params: { id: @folder1.id } - assert_response :not_found - end - - def test_authorize_member_no_module - @project1.disable_module! :dmsf - get :new, params: { id: @folder1.id } - assert_response :not_found - end - - def test_authorize_forbidden - @role_manager.remove_permission! :folder_manipulation - get :new, params: { id: @folder1.id } - assert_response :forbidden - end - - def test_target_folder - get :new, params: { id: @folder1.id, target_folder_id: @folder2.id } - assert_response :success - assert_template 'new' - end - - def test_target_folder_forbidden - @role_manager.remove_permission! :view_dmsf_folders - get :new, params: { id: @folder1.id, target_folder_id: @folder2.id } - assert_response :not_found - end - - def test_target_project - get :new, params: { id: @folder1.id, target_project_id: @project1.id } - assert_response :success - assert_template 'new' - end - - def test_new - get :new, params: { id: @folder1.id } - assert_response :success - assert_template 'new' - end - - def test_copy - post :copy, params: { id: @folder1.id, dmsf_file_or_folder: { target_project_id: @project1.id, - target_folder_id: @folder6.id } } - assert_response :redirect - assert_nil flash[:error] - end - - def test_copy_to_another_project - @request.session[:user_id] = @admin.id - assert_equal @project1.id, @folder1.project_id - post :copy, params: { id: @folder1.id, dmsf_file_or_folder: { target_project_id: @project2.id } } - assert_response :redirect - assert_nil flash[:error] - end - - def test_copy_the_same_target - post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder6.project.id } } - assert_equal flash[:error], l(:error_target_folder_same) - assert_redirected_to action: :new, target_project_id: @folder6.project.id - end - - def test_copy_to_locked_folder - @request.session[:user_id] = @admin.id - post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder2.project.id, - target_folder_id: @folder2.id } } - assert_response :forbidden - end - - def test_copy_to_dmsf_not_enabled - @project2.disable_module! :dmsf - post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } - assert_response :forbidden - end - - def test_copy_to_dmsf_enabled - post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } - assert_response :redirect - assert_nil flash[:error] - end - - def test_copy_to_as_non_member - @request.session[:user_id] = @someone.id - post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder1.project.id, - target_folder_id: @folder1.id } } - assert_response :not_found - end - - def test_move - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder1.project.id, - target_folder_id: @folder1.id } } - assert_response :redirect - assert_nil flash[:error] - end - - def test_move_to_another_project - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } - assert_response :redirect - assert_nil flash[:error] - end - - def test_move_the_same_target - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder6.project.id } } - assert_equal flash[:error], l(:error_target_folder_same) - assert_redirected_to action: :new, target_project_id: @folder6.project.id - end - - def test_move_locked_folder - @request.session[:user_id] = @admin.id - post :move, params: { id: @folder2.id, dmsf_file_or_folder: { target_project_id: @folder6.project.id, - target_folder_id: @folder6.id } } - assert_response :forbidden - end - - def test_move_to_locked_folder - @request.session[:user_id] = @admin.id - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder2.project.id, - target_folder_id: @folder2.id } } - assert_response :forbidden - end - - def test_move_to_dmsf_not_enabled - @project2.disable_module! :dmsf - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } - assert_response :forbidden - end - - def test_move_to_dmsf_enabled - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } - assert_response :redirect - assert_nil flash[:error] - end - - def test_move_to_as_non_member - @request.session[:user_id] = @someone.id - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder1.project.id, - target_folder_id: @folder1.id } } - assert_response :not_found - end - - def test_new_fast_links_enabled - member = Member.find_by(user_id: @jsmith.id, project_id: @project1.id) - assert member - member.dmsf_fast_links = true - member.save - get :new, params: { id: @folder1.id } - assert_response :success - assert_select 'label', { count: 0, text: l(:label_target_project) } - assert_select 'label', { count: 0, text: "#{l(:label_target_folder)}#" } - end - - def test_move_fast_links_enabled - # Target project is not given - post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_folder_id: @folder1.id } } - assert_response :redirect - assert_nil flash[:error] - end -end diff --git a/test/unit/dmsf_folder_test.rb b/test/unit/dmsf_folder_test.rb index 4f58eec9..b0f04da9 100644 --- a/test/unit/dmsf_folder_test.rb +++ b/test/unit/dmsf_folder_test.rb @@ -292,4 +292,12 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest assert @folder8.save assert_not DmsfFolder.where(id: @folder8.id).issystem.exists? end + + def test_any_child + # folder1 + ## folder 2 + # folder6 + assert_not @folder1.any_child?(@folder6) + assert @folder1.any_child?(@folder2) + end end