Merge pull request #664 from carlolars/webdav_fixes

Webdav various fixes
This commit is contained in:
Karel Picman 2017-02-15 11:37:58 +01:00 committed by GitHub
commit 09828943d3
20 changed files with 189 additions and 29 deletions

View File

@ -350,4 +350,4 @@ cs:
label_last_revision_id: Revize
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -347,4 +347,4 @@ de:
note_webdav_ignore: A regular expresion with filenames to ignore by PUT requests.
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ en:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ es:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ fr:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ it: # Italian strings thx 2 Matteo Arceci!
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ ja:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ pl:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ pt-BR:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ ru:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ sl:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ zh-TW:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -350,4 +350,4 @@ zh:
label_last_revision_id: Revision
label_webdav_disable_versioning: No versioning files patterns
note_webdav_disable_versioning: A regular expression that disables versioning for matching files.
note_webdav_disable_versioning: A regular expression that disables versioning for matching files. The default pattern matches temporary files created by MsOffice.

View File

@ -0,0 +1,5 @@
class AddDmsfFileLastRevisionIdToDmsfLock < ActiveRecord::Migration
def change
rename_column :dmsf_locks, :revision, :dmsf_file_last_revision_id
end
end

View File

@ -69,8 +69,7 @@ module RedmineDmsf
l.lock_scope = scope
l.user = User.current
l.expires_at = expire
# TODO: @carlolars
# l.revision = self.last_revision.id unless self.is_a?(DmsfFolder)
l.dmsf_file_last_revision_id = self.last_revision.id if self.is_a?(DmsfFile)
l.save!
reload
locks.reload

View File

@ -56,12 +56,12 @@ module RedmineDmsf
end
def self.init_testcache
puts "Webdav::Cache: Enable MemoryStore cache."
#puts "Webdav::Cache: Enable MemoryStore cache."
@@WebDAVCache = ActiveSupport::Cache::MemoryStore.new(:namespace => "RedmineDmsfWebDAV")
end
def self.init_nullcache
puts "Webdav::Cache: Disable cache."
#puts "Webdav::Cache: Disable cache."
@@WebDAVCache = ActiveSupport::Cache::NullStore.new
end

View File

@ -231,11 +231,10 @@ module RedmineDmsf
def delete
if file
raise Forbidden unless User.current.admin? || User.current.allowed_to?(:file_delete, project)
if file.name.match(/.\.tmp$/i)
# .tmp files should be destroyed (MsOffice file)
destroy = true
elsif file.name.match(/^\~\$/i)
# Files starting with ~$ should be destroyed (MsOffice file)
pattern = Setting.plugin_redmine_dmsf['dmsf_webdav_disable_versioning']
if !pattern.blank? && basename.match(pattern)
# Files that are not versioned should be destroyed
destroy = true
elsif file.last_revision.size == 0
# Zero-sized files should be destroyed
@ -479,19 +478,19 @@ module RedmineDmsf
e.add_failure @path, Conflict
raise e
end
l.expires_at = Time.now + 1.hour
l.expires_at = Time.now + 1.week
l.save!
@response['Lock-Token'] = l.uuid
return [1.hours.to_i, l.uuid]
return [1.weeks.to_i, l.uuid]
end
scope = "scope_#{(args[:scope] || 'exclusive')}".to_sym
type = "type_#{(args[:type] || 'write')}".to_sym
#l should be the instance of the lock we've just created
l = entity.lock!(scope, type, Time.now + 1.hours)
l = entity.lock!(scope, type, Time.now + 1.weeks)
@response['Lock-Token'] = l.uuid
[1.hours.to_i, l.uuid]
[1.week.to_i, l.uuid]
end
rescue DmsfLockError
e = DAV4Rack::LockFailure.new
@ -745,7 +744,8 @@ private
next if lock.expired?
# lock should be exclusive but just in case make sure we find this users lock
next if lock.user != User.current
if lock.revision != file.last_revision.id
if lock.dmsf_file_last_revision_id < file.last_revision.id
# At least one new revision has been created since the lock was created, reuse that revision.
return true
end
end

View File

@ -212,5 +212,18 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest
@file1.reload
assert !@file1.deleted?, "File #{@file1.name} is expected to exist"
end
def test_non_versioned_file
Setting.plugin_redmine_dmsf['dmsf_webdav_disable_versioning'] = '\.tmp$'
@project1.enable_module! :dmsf
@role.add_permission! :view_dmsf_folders
@role.add_permission! :file_delete
file = @file1.copy_to_filename(@project1, nil, "delete_test.tmp")
delete "/dmsf/webdav/#{@project1.identifier}/delete_test.tmp", nil, @jsmith
# The file should be destroyed
assert_nil DmsfFile.find_file_by_name(@project1, nil, "delete_test.tmp")
end
end

View File

@ -0,0 +1,118 @@
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2012 Daniel Munn <dan.munn@munnster.co.uk>
# Copyright (C) 2011-17 Karel Picman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
require 'fileutils'
class DmsfWebdavMoveTest < RedmineDmsf::Test::IntegrationTest
fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles,
:enabled_modules, :dmsf_folders, :dmsf_files, :dmsf_file_revisions
def setup
@admin = credentials 'admin'
@jsmith = credentials 'jsmith'
@project1 = Project.find_by_id 1
# Fix permissions for jsmith's role
@role = Role.find 1 #
@role.add_permission! :view_dmsf_folders
@role.add_permission! :folder_manipulation
Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1'
Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE'
super
end
def test_lock_file_already_locked_by_other
file = DmsfFile.find_by_id 1
log_user 'admin', 'admin' # login as admin
assert !User.current.anonymous?, 'Current user is anonymous'
assert file.lock!, "File failed to be locked by #{User.current.name}"
xml_http_request :lock, "/dmsf/webdav/#{@project1.identifier}/#{file.name}",
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<d:lockinfo xmlns:d=\"DAV:\">
<d:lockscope><d:exclusive/></d:lockscope>
<d:locktype><d:write/></d:locktype>
<d:owner>jsmith</d:owner>
</d:lockinfo>",
@jsmith.merge!({:HTTP_DEPTH=>"infinity",
:HTTP_TIMEOUT=>"Infinite",})
assert_response 423 # Locked
end
def test_lock_file
file = DmsfFile.find_by_id 1
create_time = Time.utc(2000, 1, 2, 3, 4, 5)
refresh_time = Time.utc(2000, 1, 2, 6, 7, 8)
# Time travel, will make the usec part of the time 0
travel_to create_time do
# Lock file
xml_http_request :lock, "/dmsf/webdav/#{@project1.identifier}/#{file.name}",
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<d:lockinfo xmlns:d=\"DAV:\">
<d:lockscope><d:exclusive/></d:lockscope>
<d:locktype><d:write/></d:locktype>
<d:owner>jsmith</d:owner>
</d:lockinfo>",
@jsmith.merge!({:HTTP_DEPTH=>"infinity",
:HTTP_TIMEOUT=>"Infinite",})
assert_response 200
# Verify the response
assert_match "<D:lockscope><D:exclusive/></D:lockscope>", response.body
assert_match "<D:locktype><D:write/></D:locktype>", response.body
assert_match "<D:depth>infinity</D:depth>", response.body
# 1.week = 7*24*3600=604800 seconds
assert_match "<D:timeout>Second-604800</D:timeout>", response.body
assert_match /<D:locktoken><D:href>([a-z0-9\-]+)<\/D:href><\/D:locktoken>/, response.body
# Extract the locktoken, needed when refreshing the lock
response.body.match(/<D:locktoken><D:href>([a-z0-9\-]+)<\/D:href><\/D:locktoken>/)
locktoken=$1
# Verify the lock in the db
l = DmsfFile.find_by_id(1).lock.first
assert_equal l.created_at, create_time
assert_equal l.updated_at, create_time
assert_equal l.expires_at, create_time + 1.week
travel_to refresh_time do
# Refresh lock
xml_http_request :lock, "/dmsf/webdav/#{@project1.identifier}/#{file.name}",
nil,
@jsmith.merge!({:HTTP_DEPTH=>"infinity",
:HTTP_TIMEOUT=>"Infinite",
:HTTP_IF=>"(#{locktoken})"})
assert_response 200
# 1.week = 7*24*3600=604800 seconds
assert_match "<D:timeout>Second-604800</D:timeout>", response.body
# Verify the lock in the db
l = DmsfFile.find_by_id(1).lock.first
assert_equal l.created_at, create_time
assert_equal l.updated_at, refresh_time
assert_equal l.expires_at, refresh_time + 1.week
end
end
end
end

View File

@ -196,10 +196,35 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest
@project1.enable_module! :dmsf # Flag module enabled
@role.add_permission! :view_dmsf_folders
@role.add_permission! :file_manipulation
log_user 'jsmith', 'jsmith' # login as jsmith
assert !User.current.anonymous?, 'Current user is not anonymous'
file = DmsfFile.find_file_by_name @project1, nil, 'test.txt'
assert file.lock!, "File failed to be locked by #{User.current.name}"
assert l=file.lock!, "File failed to be locked by #{User.current.name}"
assert_equal file.last_revision.id, l.dmsf_file_last_revision_id
# First PUT should always create new revision.
assert_difference 'file.dmsf_file_revisions.count', +1 do
put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text})
assert_response :success # 201 - Created
end
# Second PUT on a locked file should only update the revision that were created on the first PUT
assert_no_difference 'file.dmsf_file_revisions.count' do
put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text})
assert_response :success # 201 - Created
end
# Unlock
assert file.unlock!, "File failed to be unlocked by #{User.current.name}"
# Lock file again, but this time delete the revision that were stored in the lock
file = DmsfFile.find_file_by_name @project1, nil, 'test.txt'
assert l=file.lock!, "File failed to be locked by #{User.current.name}"
assert_equal file.last_revision.id, l.dmsf_file_last_revision_id
# Delete the last revision, the revision that were stored in the lock.
file.last_revision.delete(true)
# First PUT should always create new revision.
assert_difference 'file.dmsf_file_revisions.count', +1 do
put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text})
assert_response :success # 201 - Created
@ -211,7 +236,7 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest
end
end
end
def test_put_ignored_files_default
# Ignored patterns: /^(\._|\.DS_Store$|Thumbs.db$)/
if Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] == 'WEBDAV_READ_WRITE'