Multiple fixes: upload not rendered for DMSF root; dmsf_lock is now storing uuid for lock; db upgrade / downgrade script updated for support
This commit is contained in:
parent
6237e09581
commit
b8716a5cc2
@ -11,6 +11,7 @@ Changelog for Redmine DMSF
|
|||||||
* Update: Webdav locks files for 1 hour at a time (requested time is ignored)
|
* Update: Webdav locks files for 1 hour at a time (requested time is ignored)
|
||||||
* New: Files are now stored in project relevent folder
|
* New: Files are now stored in project relevent folder
|
||||||
* New: Implementation of lockdiscovery and supportedlock property requests
|
* New: Implementation of lockdiscovery and supportedlock property requests
|
||||||
|
* New: Locks store a timestamp based UUID string enabling better interaction with webservices
|
||||||
|
|
||||||
1.4.3: *2012-06-26*
|
1.4.3: *2012-06-26*
|
||||||
-----------------
|
-----------------
|
||||||
|
|||||||
@ -17,8 +17,8 @@
|
|||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
class DmsfLock < ActiveRecord::Base
|
class DmsfLock < ActiveRecord::Base
|
||||||
unloadable
|
# unloadable
|
||||||
|
before_create :generate_uuid
|
||||||
belongs_to :file, :class_name => "DmsfFile", :foreign_key => "entity_id"
|
belongs_to :file, :class_name => "DmsfFile", :foreign_key => "entity_id"
|
||||||
belongs_to :folder, :class_name => "DmsfFolder", :foreign_key => "entity_id"
|
belongs_to :folder, :class_name => "DmsfFolder", :foreign_key => "entity_id"
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
@ -46,8 +46,28 @@ class DmsfLock < ActiveRecord::Base
|
|||||||
return expires_at <= Time.now
|
return expires_at <= Time.now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def generate_uuid
|
||||||
|
self.uuid = UUIDTools::UUID.timestamp_create().to_s
|
||||||
|
end
|
||||||
|
|
||||||
def self.delete_expired
|
def self.delete_expired
|
||||||
self.delete_all ['expires_at IS NOT NULL && expires_at < ?', Time.now]
|
self.delete_all ['expires_at IS NOT NULL && expires_at < ?', Time.now]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#Lets allow our UUID to be searchable
|
||||||
|
def self.find(*args)
|
||||||
|
if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
|
||||||
|
lock = find_by_uuid(*args)
|
||||||
|
raise ActiveRecord::RecordNotFound, "Couldn't find lock with uuid=#{args.first}" if lock.nil?
|
||||||
|
lock
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_by_param(*args)
|
||||||
|
self.find(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -315,5 +315,6 @@ sUrl = "jquery.dataTables/#{I18n.locale.to_s.downcase}.json" if I18n.locale && !
|
|||||||
</script>
|
</script>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render(:partial => "multi_upload") if (User.current.allowed_to?(:file_manipulation, @project) && (!@folder.nil? && !@folder.locked_for_user?)) %>
|
<%= render(:partial => "multi_upload") if (User.current.allowed_to?(:file_manipulation, @project) &&
|
||||||
|
( @folder.nil? || (!@folder.nil? &&!@folder.locked_for_user?) ) ) %>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
|
require 'uuidtools'
|
||||||
|
|
||||||
class Dmsf144 < ActiveRecord::Migration
|
class Dmsf144 < ActiveRecord::Migration
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ class Dmsf144 < ActiveRecord::Migration
|
|||||||
#Add our lock relevent columns (ENUM) - null (till we upgrade data)
|
#Add our lock relevent columns (ENUM) - null (till we upgrade data)
|
||||||
add_column :dmsf_file_locks, :lock_type_cd, :integer, :null => true
|
add_column :dmsf_file_locks, :lock_type_cd, :integer, :null => true
|
||||||
add_column :dmsf_file_locks, :lock_scope_cd, :integer, :null => true
|
add_column :dmsf_file_locks, :lock_scope_cd, :integer, :null => true
|
||||||
|
add_column :dmsf_file_locks, :uuid, :string, :null => true, :limit => 36
|
||||||
|
|
||||||
#Add our expires_at column
|
#Add our expires_at column
|
||||||
add_column :dmsf_file_locks, :expires_at, :datetime, :null => true
|
add_column :dmsf_file_locks, :expires_at, :datetime, :null => true
|
||||||
@ -45,10 +47,20 @@ class Dmsf144 < ActiveRecord::Migration
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#Generate new lock Id's for whats being persisted
|
||||||
|
do_not_delete.each {|l|
|
||||||
|
#Find the lock
|
||||||
|
next unless lock = DmsfFileLock.find(l)
|
||||||
|
lock.uuid = UUIDTools::UUID.timestamp_create().to_s
|
||||||
|
lock.save!
|
||||||
|
}
|
||||||
|
|
||||||
say "Preserving #{do_not_delete.count} file lock(s) found in old schema"
|
say "Preserving #{do_not_delete.count} file lock(s) found in old schema"
|
||||||
|
|
||||||
DmsfFileLock.delete_all(['id NOT IN (?)', do_not_delete])
|
DmsfFileLock.delete_all(['id NOT IN (?)', do_not_delete])
|
||||||
|
|
||||||
|
#We need to force our newly found
|
||||||
|
|
||||||
say "Applying default lock scope / type - Exclusive / Write"
|
say "Applying default lock scope / type - Exclusive / Write"
|
||||||
DmsfFileLock.update_all ['entity_type = ?, lock_type_cd = ?, lock_scope_cd = ?', 0, 0, 0]
|
DmsfFileLock.update_all ['entity_type = ?, lock_type_cd = ?, lock_scope_cd = ?', 0, 0, 0]
|
||||||
|
|
||||||
@ -101,6 +113,7 @@ class Dmsf144 < ActiveRecord::Migration
|
|||||||
remove_column :dmsf_file_locks, :lock_type_cd
|
remove_column :dmsf_file_locks, :lock_type_cd
|
||||||
remove_column :dmsf_file_locks, :lock_scope_cd
|
remove_column :dmsf_file_locks, :lock_scope_cd
|
||||||
remove_column :dmsf_file_locks, :expires_at
|
remove_column :dmsf_file_locks, :expires_at
|
||||||
|
remove_column :dmsf_file_locks, :uuid
|
||||||
|
|
||||||
#Not sure if this is the right place to do this, as its file manipulation, not database (stricly)
|
#Not sure if this is the right place to do this, as its file manipulation, not database (stricly)
|
||||||
begin
|
begin
|
||||||
|
|||||||
@ -26,23 +26,18 @@ module RedmineDmsf
|
|||||||
existing = locks(false)
|
existing = locks(false)
|
||||||
raise DmsfLockError.new("Unable to complete lock - resource (or parent) is locked") if self.locked? && existing.empty?
|
raise DmsfLockError.new("Unable to complete lock - resource (or parent) is locked") if self.locked? && existing.empty?
|
||||||
unless existing.empty?
|
unless existing.empty?
|
||||||
if existing[0].lock_scope == :scope_exclusive
|
if existing[0].lock_scope == :scope_shared && scope == :scope_shared
|
||||||
# If it's an exclusive lock and you're re-requesting the actual desired behaviour is to not return a new lock,
|
# RFC states if an item is exclusively locked and another lock is attempted we reject
|
||||||
# but the same lock (extended)
|
# if the item is shared locked however, we can always add another lock to it
|
||||||
if self.folder.locked?
|
if self.folder.locked?
|
||||||
raise DmsfLockError.new("Unable to complete lock - resource parent is locked")
|
raise DmsfLockError.new("Unable to complete lock - resource parent is locked")
|
||||||
else
|
else
|
||||||
if existing[0].user.id == User.current.id then
|
existing.each {|l|
|
||||||
l = existing[0]
|
raise DmsfLockError.new("Unable to complete lock - resource is locked") if l.user.id == User.current.id
|
||||||
l.expires_at = expire
|
}
|
||||||
l.save!
|
|
||||||
return l
|
|
||||||
else
|
|
||||||
raise DmsfLockError.new("Unable to complete lock - resource is locked")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise DmsfLockError.new("unable to exclusively lock a shared-locked resource") if scope == :scope_exclusive
|
raise DmsfLockError.new("unable to lock exclusively an already-locked resource") if scope == :scope_exclusive
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
l = DmsfLock.new
|
l = DmsfLock.new
|
||||||
|
|||||||
@ -398,7 +398,6 @@ module RedmineDmsf
|
|||||||
# Lock
|
# Lock
|
||||||
def lock(args)
|
def lock(args)
|
||||||
return Conflict unless (parent.projectless_path == "/" || parent_exists?)
|
return Conflict unless (parent.projectless_path == "/" || parent_exists?)
|
||||||
token = UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, projectless_path).to_s
|
|
||||||
lock_check(args[:scope])
|
lock_check(args[:scope])
|
||||||
entity = file? ? file : folder
|
entity = file? ? file : folder
|
||||||
begin
|
begin
|
||||||
@ -416,17 +415,12 @@ module RedmineDmsf
|
|||||||
return Conflict if http_if.nil?
|
return Conflict if http_if.nil?
|
||||||
|
|
||||||
http_if = http_if.slice(1, http_if.length - 2)
|
http_if = http_if.slice(1, http_if.length - 2)
|
||||||
|
l = DmsfLock.find(http_if)
|
||||||
return Conflict unless http_if == token
|
return Conflict if l.nil?
|
||||||
|
l.expires_at = Time.now + 1.hour
|
||||||
entity.lock(false).each {|l|
|
l.save!
|
||||||
if l.user.id == User.current.id
|
@response['Lock-Token'] = l.uuid
|
||||||
l.expires_at = Time.now + 1.hour
|
return [1.hours.to_i, l.uuid]
|
||||||
l.save!
|
|
||||||
@response['Lock-Token'] = token
|
|
||||||
return [1.hours.to_i, token]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
#Unfortunately if we're here, then it's updating a lock we can't find
|
#Unfortunately if we're here, then it's updating a lock we can't find
|
||||||
|
|
||||||
@ -436,9 +430,10 @@ module RedmineDmsf
|
|||||||
scope = "scope_#{(args[:scope] || "exclusive")}".to_sym
|
scope = "scope_#{(args[:scope] || "exclusive")}".to_sym
|
||||||
type = "type_#{(args[:type] || "write")}".to_sym
|
type = "type_#{(args[:type] || "write")}".to_sym
|
||||||
|
|
||||||
entity.lock! scope, type, Time.now + 1.hours
|
#l should be the instance of the lock we've just created
|
||||||
@response['Lock-Token'] = token
|
l = entity.lock!(scope, type, Time.now + 1.hours)
|
||||||
[1.hours.to_i, token]
|
@response['Lock-Token'] = l.uuid
|
||||||
|
[1.hours.to_i, l.uuid]
|
||||||
end
|
end
|
||||||
rescue DmsfLockError
|
rescue DmsfLockError
|
||||||
raise DAV4Rack::LockFailure.new("Failed to lock: #{@path}")
|
raise DAV4Rack::LockFailure.new("Failed to lock: #{@path}")
|
||||||
@ -455,9 +450,12 @@ module RedmineDmsf
|
|||||||
BadRequest
|
BadRequest
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
_token = UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, projectless_path).to_s
|
|
||||||
entity = file? ? file : folder
|
entity = file? ? file : folder
|
||||||
if (!entity.locked? || entity.locked_for_user? || token != _token)
|
l = DmsfLock.find(token)
|
||||||
|
l_entity = l.file || l.folder
|
||||||
|
# Additional case: if a user trys to unlock the file instead of the folder that's locked
|
||||||
|
# This should throw forbidden as only the lock at level initiated should be unlocked
|
||||||
|
if (!entity.locked? || entity.locked_for_user? || l_entity != entity)
|
||||||
Forbidden
|
Forbidden
|
||||||
else
|
else
|
||||||
entity.unlock!
|
entity.unlock!
|
||||||
@ -607,6 +605,9 @@ module RedmineDmsf
|
|||||||
lock_path << lock_entity.dmsf_path.map {|x| URI.escape(x.respond_to?('name') ? x.name : x.title) }.join('/')
|
lock_path << lock_entity.dmsf_path.map {|x| URI.escape(x.respond_to?('name') ? x.name : x.title) }.join('/')
|
||||||
lock_path << "/" if lock_entity.is_a?(DmsfFolder) && lock_path[-1,1] != '/'
|
lock_path << "/" if lock_entity.is_a?(DmsfFolder) && lock_path[-1,1] != '/'
|
||||||
doc.lockroot { doc.href lock_path }
|
doc.lockroot { doc.href lock_path }
|
||||||
|
if (lock.user.id == User.current.id || User.current.allowed_to?(:force_file_unlock, self.project))
|
||||||
|
doc.locktoken { doc.href lock.uuid }
|
||||||
|
end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user