diff --git a/app/models/dmsf_folder.rb b/app/models/dmsf_folder.rb index b1284836..f2342a1a 100644 --- a/app/models/dmsf_folder.rb +++ b/app/models/dmsf_folder.rb @@ -43,7 +43,7 @@ class DmsfFolder < ActiveRecord::Base has_many :locks, -> { where(entity_type: 1).order("#{DmsfLock.table_name}.updated_at DESC") }, :class_name => 'DmsfLock', :foreign_key => 'entity_id', :dependent => :destroy - INVALID_CHARACTERS = /\A[^\/\\\?":<>]*\z/.freeze + INVALID_CHARACTERS = /\A[^\/\\\?":<>#%\*]*\z/.freeze STATUS_DELETED = 1.freeze STATUS_ACTIVE = 0.freeze AVAILABLE_COLUMNS = %w(id title extension size modified version workflow author).freeze diff --git a/app/views/settings/_dmsf_settings.html.erb b/app/views/settings/_dmsf_settings.html.erb index ff5b56d2..62022bc8 100644 --- a/app/views/settings/_dmsf_settings.html.erb +++ b/app/views/settings/_dmsf_settings.html.erb @@ -202,6 +202,15 @@

+

+ <%= content_tag(:label, l(:label_webdav_use_project_names)) %> + <%= check_box_tag('settings[dmsf_webdav_use_project_names]', true, @settings['dmsf_webdav_use_project_names']) %> + + <%= l(:note_webdav_use_project_names) %>
+ <%= l(:label_default)%>: <%= l(:general_text_No)%> +
+

+
<%= l(:label_full_text) %> diff --git a/config/locales/cs.yml b/config/locales/cs.yml index d8d62549..dc7ddc0d 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -355,4 +355,7 @@ cs: label_dmsf_keep_documents_locked: Ponechat dokumenty zamčené note_dmsf_keep_documents_locked: Dokumenty zůstanou i po schválení zamčené. note_global: (globální) - field_dmsf_not_inheritable: Není dědičné \ No newline at end of file + field_dmsf_not_inheritable: Není dědičné + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/de.yml b/config/locales/de.yml index dcfe7a49..3d9d029f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -352,4 +352,7 @@ de: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/en.yml b/config/locales/en.yml index 0d8f245a..4f909279 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -356,3 +356,6 @@ en: note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/es.yml b/config/locales/es.yml index 0cac72fe..3194918b 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -355,4 +355,7 @@ es: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index b9929766..1e48b134 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -355,4 +355,7 @@ fr: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/it.yml b/config/locales/it.yml index ae10895f..50b1e09d 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -355,4 +355,7 @@ it: # Italian strings thx 2 Matteo Arceci! label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 2b800a59..4a97a71d 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -355,4 +355,7 @@ ja: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 19e94eb7..2ee7d34d 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -355,4 +355,7 @@ pl: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d7cd3e24..e63bf12a 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -355,4 +355,7 @@ pt-BR: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 7be56919..7fb2c19f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -355,4 +355,7 @@ ru: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 9cbace5d..d7a10f6f 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -355,4 +355,7 @@ sl: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index f191b8cd..369c869e 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -355,4 +355,7 @@ zh-TW: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 6b3a6ec6..65aa5a9b 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -355,4 +355,7 @@ zh: label_dmsf_keep_documents_locked: Keep documents locked note_dmsf_keep_documents_locked: Documents will be kept locked when approved note_global: (global) - field_dmsf_not_inheritable: Not inheritable \ No newline at end of file + field_dmsf_not_inheritable: Not inheritable + + label_webdav_use_project_names: Use project name for project folder + note_webdav_use_project_names: Use project names instead of project identifier for project folders. diff --git a/lib/redmine_dmsf/webdav/base_resource.rb b/lib/redmine_dmsf/webdav/base_resource.rb index ec4c59e6..fbd67438 100644 --- a/lib/redmine_dmsf/webdav/base_resource.rb +++ b/lib/redmine_dmsf/webdav/base_resource.rb @@ -93,6 +93,22 @@ module RedmineDmsf new_path = '/' + new_path unless new_path[0,1] == '/' @__proxy.class.new("#{new_public}#{name}", "#{new_path}#{name}", request, response, options.merge(:user => @user)) end + + def child_project(p) + project_display_name = ProjectResource.create_project_name(p) + + new_public = public_path.dup + new_public = new_public + '/' unless new_public[-1,1] == '/' + new_public = '/' + new_public unless new_public[0,1] == '/' + new_public += project_display_name + + new_path = path.dup + new_path = new_path + '/' unless new_path[-1,1] == '/' + new_path = '/' + new_path unless new_path[0,1] == '/' + new_path += project_display_name + + @__proxy.class.new("#{new_public}", "#{new_path}", request, response, options.merge(:user => @user)) + end def parent p = @__proxy.parent @@ -148,12 +164,27 @@ module RedmineDmsf # Return instance of Project based on the path def project unless @project + use_project_names = Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] pinfo = @path.split('/').drop(1) if pinfo.length > 0 - begin - @project = Project.find(pinfo.first) - rescue Exception => e - Rails.logger.warn e.message + if use_project_names + unless pinfo.first.match('(\[([0-9]+)\])$').nil? + pid = $2 + begin + @project = Project.find_by_id(pid) + rescue Exception => e + Rails.logger.error e.message + end + end + if @project.nil? + Rails.logger.warn {"WebDAV ERROR: No project found on path '#{@path}'"} + end + else + begin + @project = Project.find(pinfo.first) + rescue Exception => e + Rails.logger.warn e.message + end end end end diff --git a/lib/redmine_dmsf/webdav/index_resource.rb b/lib/redmine_dmsf/webdav/index_resource.rb index 796e7914..bf09e329 100644 --- a/lib/redmine_dmsf/webdav/index_resource.rb +++ b/lib/redmine_dmsf/webdav/index_resource.rb @@ -31,10 +31,10 @@ module RedmineDmsf def children unless @projects @projects = [] - Project.select(:identifier).has_module(:dmsf).where( + Project.select(:id, :identifier, :name).has_module(:dmsf).where( Project.allowed_to_condition( User.current, :view_dmsf_folders)).order('lft').all.each do |p| - @projects << child(p.identifier) + @projects << child_project(p) end end @projects diff --git a/lib/redmine_dmsf/webdav/project_resource.rb b/lib/redmine_dmsf/webdav/project_resource.rb index 02d91627..aa6dc3f6 100644 --- a/lib/redmine_dmsf/webdav/project_resource.rb +++ b/lib/redmine_dmsf/webdav/project_resource.rb @@ -28,7 +28,7 @@ module RedmineDmsf @children = nil end - def children + def children unless @children @children = [] if project @@ -72,7 +72,7 @@ module RedmineDmsf end def name - project.identifier unless project.nil? + ProjectResource.create_project_name(project) end def long_name @@ -108,7 +108,21 @@ module RedmineDmsf def project_id self.project.id if self.project end + + # Characters that MATCH this regex will be replaced with dots, no more than one dot in a row. + INVALID_CHARACTERS = /[\/\\\?":<>#%\*]/.freeze # = / \ ? " : < > # % * + def self.create_project_name(p) + use_project_names = Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] + if use_project_names + # 1. Invalid characters are replaced with a dot. + # 2. Two or more dots in a row are replaced with a single dot. + # (3. Windows WebClient does not like a dot at the end, but since the project id tag is appended this is not a problem.) + "#{p.name.gsub(INVALID_CHARACTERS, ".").gsub(/\.{2,}/, ".")} [#{p.id}]" unless p.nil? + else + p.identifier unless p.nil? + end + end end end end diff --git a/test/integration/webdav/dmsf_webdav_delete_test.rb b/test/integration/webdav/dmsf_webdav_delete_test.rb index 562533fc..e4274d90 100644 --- a/test/integration/webdav/dmsf_webdav_delete_test.rb +++ b/test/integration/webdav/dmsf_webdav_delete_test.rb @@ -40,6 +40,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest @file1 = DmsfFile.find_by_id 1 Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false DmsfFile.storage_path = File.expand_path '../../../fixtures/files', __FILE__ User.current = nil end @@ -169,6 +170,23 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest end end + def test_folder_delete_by_user_with_project_names + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + @role.add_permission! :view_dmsf_folders + @role.add_permission! :folder_manipulation + @project1.enable_module! :dmsf # Flag module enabled + + delete "/dmsf/webdav/#{@project1.identifier}/#{@folder6.title}", nil, @jsmith + assert_response 404 + + p1name = RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1) + p1name_uri = URI.encode(p1name, /\W/) + delete "/dmsf/webdav/#{p1name_uri}/#{@folder6.title}", nil, @jsmith + assert_response :success + @folder6.reload + assert @folder6.deleted?, "Folder #{@folder1.title} is not expected to exist" + end + def test_file_delete_by_administrator if Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] == 'WEBDAV_READ_WRITE' @project1.enable_module! :dmsf @@ -191,6 +209,25 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest end end + def test_file_delete_by_user_with_project_names + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + @project1.enable_module! :dmsf + @role.add_permission! :view_dmsf_folders + @role.add_permission! :file_delete + + delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @jsmith + assert_response 404 + + p1name = RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1) + p1name_uri = URI.encode(p1name, /\W/) + + delete "/dmsf/webdav/#{p1name_uri}/#{@file1.name}", nil, @jsmith + assert_response :success + + @file1.reload + assert @file1.deleted?, "File #{@file1.name} is not expected to exist" + end + def test_locked_folder @project1.enable_module! :dmsf # Flag module enabled @role.add_permission! :view_dmsf_folders diff --git a/test/integration/webdav/dmsf_webdav_get_test.rb b/test/integration/webdav/dmsf_webdav_get_test.rb index 87b0f250..a06b3195 100644 --- a/test/integration/webdav/dmsf_webdav_get_test.rb +++ b/test/integration/webdav/dmsf_webdav_get_test.rb @@ -34,6 +34,7 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest @role = Role.find_by_id 1 # Manager Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false DmsfFile.storage_path = File.expand_path '../../../fixtures/files', __FILE__ User.current = nil end @@ -62,13 +63,21 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest def test_should_list_dmsf_enabled_project get '/dmsf/webdav', nil, @admin assert_response :success - assert !response.body.match(@project1.name).nil?, "Expected to find project #{@project1.name} in return data" + assert !response.body.match(@project1.identifier).nil?, "Expected to find project #{@project1.identifier} in return data" + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1) + + get '/dmsf/webdav', nil, @admin + assert_response :success + assert_no_match @project1.identifier, response.body + assert_match project1_uri, response.body end def test_should_not_list_non_dmsf_enabled_project get '/dmsf/webdav', nil, @jsmith assert_response :success - assert response.body.match(@project2.name).nil?, "Unexpected find of project #{@project2.name} in return data" + assert response.body.match(@project2.identifier).nil?, "Unexpected find of project #{@project2.identifier} in return data" end def test_should_return_status_404_when_project_does_not_exist @@ -85,6 +94,15 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest def test_download_file_from_dmsf_enabled_project get "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin assert_response :success + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + + get "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin + assert_response 404 + + get "/dmsf/webdav/#{project1_uri}/test.txt", nil, @admin + assert_response :success end def test_should_list_dmsf_contents_within_project diff --git a/test/integration/webdav/dmsf_webdav_head_test.rb b/test/integration/webdav/dmsf_webdav_head_test.rb index 9c4e6ec9..5f38370b 100644 --- a/test/integration/webdav/dmsf_webdav_head_test.rb +++ b/test/integration/webdav/dmsf_webdav_head_test.rb @@ -32,7 +32,11 @@ class DmsfWebdavHeadTest < RedmineDmsf::Test::IntegrationTest @project1 = Project.find_by_id 1 @project2 = Project.find_by_id 2 Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' - Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + # Temporarily enable project names + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + @project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false DmsfFile.storage_path = File.expand_path '../../../fixtures/files', __FILE__ User.current = nil end @@ -52,12 +56,28 @@ class DmsfWebdavHeadTest < RedmineDmsf::Test::IntegrationTest head "/dmsf/webdav/#{@project1.identifier}", nil, @admin assert_response :success check_headers_exist + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + head "/dmsf/webdav/#{@project1.identifier}", nil, @admin + assert_response 404 + + head "/dmsf/webdav/#{@project1_uri}", nil, @admin + assert_response :success end def test_head_responds_anonymous_msoffice_user_agent head "/dmsf/webdav/#{@project1.identifier}", nil, {:HTTP_USER_AGENT => "Microsoft Office Word 2014"} assert_response :success check_headers_exist + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + head "/dmsf/webdav/#{@project1.identifier}", nil, {:HTTP_USER_AGENT => "Microsoft Office Word 2014"} + assert_response 404 + + head "/dmsf/webdav/#{@project1_uri}", nil, {:HTTP_USER_AGENT => "Microsoft Office Word 2014"} + assert_response :success end def test_head_responds_anonymous_other_user_agent @@ -75,12 +95,28 @@ class DmsfWebdavHeadTest < RedmineDmsf::Test::IntegrationTest head "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin assert_response :success check_headers_exist # Note it'll allow 1 out of the 3 expected to fail + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + head "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin + assert_response 404 + + head "/dmsf/webdav/#{@project1_uri}/test.txt", nil, @admin + assert_response :success end def test_head_responds_to_file_anonymous_msoffice_user_agent head "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, {:HTTP_USER_AGENT => "Microsoft Office Word 2014"} assert_response :success check_headers_exist # Note it'll allow 1 out of the 3 expected to fail + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + head "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, {:HTTP_USER_AGENT => "Microsoft Office Word 2014"} + assert_response 404 + + head "/dmsf/webdav/#{@project1_uri}/test.txt", nil, {:HTTP_USER_AGENT => "Microsoft Office Word 2014"} + assert_response :success end def test_head_responds_to_file_anonymous_other_user_agent diff --git a/test/integration/webdav/dmsf_webdav_mkcol_test.rb b/test/integration/webdav/dmsf_webdav_mkcol_test.rb index ac571671..bdaf4a67 100644 --- a/test/integration/webdav/dmsf_webdav_mkcol_test.rb +++ b/test/integration/webdav/dmsf_webdav_mkcol_test.rb @@ -35,6 +35,7 @@ class DmsfWebdavMkcolTest < RedmineDmsf::Test::IntegrationTest @folder6 = DmsfFolder.find_by_id 6 Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false DmsfFile.storage_path = File.expand_path '../../../fixtures/files', __FILE__ User.current = nil end @@ -98,12 +99,19 @@ class DmsfWebdavMkcolTest < RedmineDmsf::Test::IntegrationTest end def test_should_create_folder_for_non_admin_user_with_rights - if Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] == 'WEBDAV_READ_WRITE' - @project1.enable_module! :dmsf - @role.add_permission! :folder_manipulation - xml_http_request :mkcol, "/dmsf/webdav/#{@project1.identifier}/test1", nil, @jsmith - assert_response :success - end + @project1.enable_module! :dmsf + @role.add_permission! :folder_manipulation + xml_http_request :mkcol, "/dmsf/webdav/#{@project1.identifier}/test1", nil, @jsmith + assert_response :success + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + + xml_http_request :mkcol, "/dmsf/webdav/#{@project1.identifier}/test2", nil, @jsmith + assert_response 404 + + xml_http_request :mkcol, "/dmsf/webdav/#{project1_uri}/test3", nil, @jsmith + assert_response :success end end \ No newline at end of file diff --git a/test/integration/webdav/dmsf_webdav_move_test.rb b/test/integration/webdav/dmsf_webdav_move_test.rb index 2aa781ae..96105fd7 100644 --- a/test/integration/webdav/dmsf_webdav_move_test.rb +++ b/test/integration/webdav/dmsf_webdav_move_test.rb @@ -46,6 +46,7 @@ class DmsfWebdavMoveTest < RedmineDmsf::Test::IntegrationTest Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false super end @@ -130,6 +131,21 @@ class DmsfWebdavMoveTest < RedmineDmsf::Test::IntegrationTest assert f, "Moved file '#{new_name}' not found in project." end end + + def test_move_to_new_filename_with_project_names + file = DmsfFile.find_by_id 1 + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + + new_name = "#{file.name}.moved" + assert_difference 'file.dmsf_file_revisions.count', +1 do + xml_http_request :move, "/dmsf/webdav/#{project1_uri}/#{file.name}", nil, + @jsmith.merge!({:destination => "http://www.example.com/dmsf/webdav/#{project1_uri}/#{new_name}"}) + assert_response 201 # Created + f = DmsfFile.find_file_by_name @project1, nil, "#{new_name}" + assert f, "Moved file '#{new_name}' not found in project." + end + end def test_move_zero_sized_to_new_filename file = DmsfFile.find_by_id 10 @@ -158,6 +174,23 @@ class DmsfWebdavMoveTest < RedmineDmsf::Test::IntegrationTest assert_equal folder.id, file2.dmsf_folder_id end end + + def test_move_to_new_folder_with_project_names + file = DmsfFile.find_by_id 1 + folder = DmsfFolder.find_by_id 1 + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + assert_kind_of DmsfFile, file + assert_kind_of DmsfFolder, folder + + assert_difference 'file.dmsf_file_revisions.count', +1 do + xml_http_request :move, "/dmsf/webdav/#{project1_uri}/#{file.name}", nil, + @jsmith.merge!({:destination => "http://www.example.com/dmsf/webdav/#{project1_uri}/#{folder.title}/#{file.name}"}) + assert_response 201 # Created + file2 = DmsfFile.find_by_id 1 + assert_equal folder.id, file2.dmsf_folder_id + end + end def test_move_zero_sized_to_new_folder file = DmsfFile.find_by_id 10 diff --git a/test/integration/webdav/dmsf_webdav_options_test.rb b/test/integration/webdav/dmsf_webdav_options_test.rb index 069b2ed4..f213b495 100644 --- a/test/integration/webdav/dmsf_webdav_options_test.rb +++ b/test/integration/webdav/dmsf_webdav_options_test.rb @@ -33,6 +33,7 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest @project2 = Project.find_by_id 2 Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false end def test_truth @@ -60,8 +61,7 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest assert_response :success assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' assert response.headers['Allow'] , 'Allow header is empty or does not exist' - # TODO: Unable to set the 'WEBDAV_READ_WRITE' mode - #assert_equal response.headers['Allow'], 'OPTIONS,HEAD,GET,PROPFIND,PUT,POST,DELETE,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' + assert_equal response.headers['Allow'], 'OPTIONS,HEAD,GET,PROPFIND,PUT,POST,DELETE,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' end def test_options_returns_expected_dav_header @@ -111,8 +111,7 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest assert_response :success assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" assert response.headers['Allow'], 'Allow header is empty or does not exist' - # TODO: Unable to set the 'WEBDAV_READ_WRITE' mode - #assert_equal response.headers['Allow'], 'OPTIONS,HEAD,GET,PROPFIND,PUT,POST,DELETE,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' + assert_equal response.headers['Allow'], 'OPTIONS,HEAD,GET,PROPFIND,PUT,POST,DELETE,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' end def test_authenticated_options_returns_expected_dav_header @@ -148,18 +147,25 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest def test_authenticated_options_for_other_user_agent xml_http_request :options, "/dmsf/webdav/#{@project1.identifier}", nil, @admin.merge!({:HTTP_USER_AGENT => "Other"}) assert_response :success + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + + xml_http_request :options, "/dmsf/webdav/#{@project1.identifier}", nil, @admin.merge!({:HTTP_USER_AGENT => "Other"}) + assert_response 404 + xml_http_request :options, "/dmsf/webdav/#{project1_uri}", nil, @admin.merge!({:HTTP_USER_AGENT => "Other"}) + assert_response :success end - # TODO: It doesn't work - # def test_authenticated_options_returns_401_for_non_dmsf_enabled_items - # @project2.disable_module! :dmsf - # xml_http_request :options, "/dmsf/webdav/#{@project2.identifier}", nil, @jsmith - # assert_response 401 # refused - # end - # - # def test_authenticated_options_returns_401_for_not_found - # xml_http_request :options, '/dmsf/webdav/does-not-exist', nil, @jsmith - # assert_response 401 # refused - # end + def test_authenticated_options_returns_404_for_non_dmsf_enabled_items + @project2.disable_module! :dmsf + xml_http_request :options, "/dmsf/webdav/#{@project2.identifier}", nil, @jsmith + assert_response 404 + end + + def test_authenticated_options_returns_404_for_not_found + xml_http_request :options, '/dmsf/webdav/does-not-exist', nil, @jsmith + assert_response 404 + end end diff --git a/test/integration/webdav/dmsf_webdav_propfind_test.rb b/test/integration/webdav/dmsf_webdav_propfind_test.rb index ef997ada..68c5d2ff 100644 --- a/test/integration/webdav/dmsf_webdav_propfind_test.rb +++ b/test/integration/webdav/dmsf_webdav_propfind_test.rb @@ -20,6 +20,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require File.expand_path('../../../test_helper', __FILE__) +require 'uri' class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest @@ -42,6 +43,13 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + + # Temporarily enable project names to generate names for project1 + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + @project1_name = RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1) + @project1_uri = URI.encode(@project1_name, /\W/) + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false + RedmineDmsf::Webdav::Cache.init_nullcache end def test_truth @@ -108,6 +116,24 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest assert_no_match "#{@project2.identifier}", response.body end + def test_propfind_depth1_on_root_for_admin_with_project_names + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + xml_http_request :propfind, "/dmsf/webdav/", nil, + @admin.merge!({:HTTP_DEPTH => "1"}) + + assert_response 207 # MultiStatus + assert_match "http://www.example.com:80/dmsf/webdav/", response.body + assert_match "/", response.body + + # project.identifier should not match when using project names + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/", response.body + assert_no_match "#{@project1.identifier}", response.body + + # but the project name should match + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/", response.body + assert_match "#{@project1_name}", response.body + end + def test_propfind_depth0_on_project1_for_non_member xml_http_request :propfind, "/dmsf/webdav/#{@project1.identifier}", nil, @jsmith.merge!({:HTTP_DEPTH => "0"}) @@ -136,6 +162,25 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/#{@file10.name}", response.body assert_no_match "#{@file10.name}", response.body end + + def test_propfind_depth0_on_project1_for_admin_with_project_names + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + xml_http_request :propfind, "/dmsf/webdav/#{@project1.identifier}", nil, + @admin.merge!({:HTTP_DEPTH => "0"}) + assert_response 404 + + xml_http_request :propfind, "/dmsf/webdav/#{@project1_uri}", nil, + @admin.merge!({:HTTP_DEPTH => "0"}) + assert_response 207 # MultiStatus + assert_no_match "http://www.example.com:80/dmsf/webdav/", response.body + assert_no_match "/", response.body + # Project + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/", response.body + assert_no_match "#{@project1.identifier}", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/", response.body + assert_match "#{@project1_name}", response.body + end def test_propfind_depth1_on_project1_for_admin xml_http_request :propfind, "/dmsf/webdav/#{@project1.identifier}", nil, @@ -160,6 +205,97 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest assert_match "#{@file10.name}", response.body end + def test_propfind_depth1_on_project1_for_admin_with_project_names + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + xml_http_request :propfind, "/dmsf/webdav/#{@project1.identifier}", nil, + @admin.merge!({:HTTP_DEPTH => "1"}) + assert_response 404 + + xml_http_request :propfind, "/dmsf/webdav/#{@project1_uri}", nil, + @admin.merge!({:HTTP_DEPTH => "1"}) + assert_response 207 # MultiStatus + + assert_no_match "http://www.example.com:80/dmsf/webdav/", response.body + assert_no_match "/", response.body + + # Project + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/", response.body + assert_no_match "#{@project1.identifier}", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/", response.body + assert_match "#{@project1_name}", response.body + + # Folders + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/#{@folder1.title}/", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/#{@folder1.title}/", response.body + assert_match "#{@folder1.title}", response.body + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/#{@folder6.title}/", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/#{@folder6.title}/", response.body + assert_match "#{@folder6.title}", response.body + + # Files + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/#{@file1.name}", response.body + assert_match "#{@file1.name}", response.body + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/#{@file9.name}", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/#{@file9.name}", response.body + assert_match "#{@file9.name}", response.body + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/#{@file10.name}", response.body + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/#{@file10.name}", response.body + assert_match "#{@file10.name}", response.body + end + + def test_propfind_depth1_on_root_for_admin_with_project_names_and_cache + RedmineDmsf::Webdav::Cache.init_testcache + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + + # PROPSTATS for / and project1 should be cached. + assert_difference 'RedmineDmsf::Webdav::Cache.cache.instance_variable_get(:@data).count', +2 do + xml_http_request :propfind, "/dmsf/webdav/", nil, + @admin.merge!({:HTTP_DEPTH => "1"}) + end + + assert_response 207 # MultiStatus + assert_match "http://www.example.com:80/dmsf/webdav/", response.body + assert_match "/", response.body + + # project.identifier should not match when using project names + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/", response.body + assert_no_match "#{@project1.identifier}", response.body + + # but the project name should match + assert_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/", response.body + assert_match "#{@project1_name}", response.body + + # Rename project1 + @project1.name = "Online Cookbook" + @project1.save! + project1_new_name = RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1) + project1_new_uri = URI.encode(project1_new_name, /\W/) + + # PROPSTATS for / is already cached, but a new PROPSTATS should be cached for project1 + assert_difference 'RedmineDmsf::Webdav::Cache.cache.instance_variable_get(:@data).count', +1 do + xml_http_request :propfind, "/dmsf/webdav/", nil, + @admin.merge!({:HTTP_DEPTH => "1"}) + end + + assert_response 207 # MultiStatus + assert_match "http://www.example.com:80/dmsf/webdav/", response.body + assert_match "/", response.body + + # project.identifier should not match when using project names + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1.identifier}/", response.body + assert_no_match "#{@project1.identifier}", response.body + + # old project name should not match + assert_no_match "http://www.example.com:80/dmsf/webdav/#{@project1_uri}/", response.body + assert_no_match "#{@project1_name}", response.body + + # but new project name should match + assert_match "http://www.example.com:80/dmsf/webdav/#{project1_new_uri}/", response.body + assert_match "#{project1_new_name}", response.body + end + def test_propfind_depth0_on_project1_for_admin_with_cache RedmineDmsf::Webdav::Cache.init_testcache @@ -184,8 +320,6 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest assert_response 207 # MultiStatus end assert_match "Cached PROPSTATS/#{@project1.identifier}", response.body - - RedmineDmsf::Webdav::Cache.init_nullcache end def test_propfind_depth1_on_project1_for_admin_with_cache @@ -237,8 +371,6 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest assert_match "Cached PROPSTATS/#{@project1.identifier}", response.body assert_match "Cached PROPSTATS/#{@project1.identifier}", RedmineDmsf::Webdav::Cache.read("PROPFIND/#{@project1.id}") assert !RedmineDmsf::Webdav::Cache.exist?("PROPFIND/#{@project1.id}.invalid") - - RedmineDmsf::Webdav::Cache.init_nullcache end end \ No newline at end of file diff --git a/test/integration/webdav/dmsf_webdav_put_test.rb b/test/integration/webdav/dmsf_webdav_put_test.rb index 38a11fbf..eb5b28fe 100644 --- a/test/integration/webdav/dmsf_webdav_put_test.rb +++ b/test/integration/webdav/dmsf_webdav_put_test.rb @@ -39,6 +39,7 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest @role = Role.find 1 # Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = false super end @@ -93,13 +94,19 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest end def test_put_as_admin_granted_on_dmsf_enabled_project - if Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] == 'WEBDAV_READ_WRITE' - put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @admin.merge!({:content_type => :text}) - assert_response :success # 201 Created - # Lets check for our file - file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' - assert file, 'Check for files existance' - end + put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @admin.merge!({:content_type => :text}) + assert_response :success # 201 Created + # Lets check for our file + file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' + assert file, 'Check for files existance' + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + + put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @admin.merge!({:content_type => :text}) + assert_response 409 + put "/dmsf/webdav/#{project1_uri}/test-1234.txt", '1234', @admin.merge!({:content_type => :text}) + assert_response :success # 201 Created end def test_put_failed_as_jsmith_on_non_dmsf_enabled_project @@ -137,16 +144,21 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest end def test_put_succeeds_for_non_admin_with_correct_permissions - if Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] == 'WEBDAV_READ_WRITE' - @project1.enable_module! :dmsf # Flag module enabled - @role.add_permission! :view_dmsf_folders - @role.add_permission! :file_manipulation - put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) - assert_response :success # 201 - Now we have permissions - # Lets check for our file - file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' - assert file, 'File test-1234 was not found in projects dmsf folder.' - end + @project1.enable_module! :dmsf # Flag module enabled + @role.add_permission! :view_dmsf_folders + @role.add_permission! :file_manipulation + put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) + assert_response :success # 201 - Now we have permissions + # Lets check for our file + file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' + assert file, 'File test-1234 was not found in projects dmsf folder.' + + Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = true + project1_uri = URI.encode(RedmineDmsf::Webdav::ProjectResource.create_project_name(@project1), /\W/) + put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) + assert_response 409 + put "/dmsf/webdav/#{project1_uri}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) + assert_response :success # 201 - Now we have permissions end def test_put_writes_revision_successfully_for_unlocked_file