Pass 1 of webdav (VERY ROUGH) - Functional on Get/Head (Web based listing) and also webdav listing and downloading of resources.

User based authentication and tracking of downloads, however security model not implemented yet.
This commit is contained in:
Daniel Munn 2012-06-11 11:30:04 +01:00
parent fa4207dfce
commit 2e431568d6
5 changed files with 214 additions and 33 deletions

View File

@ -1,6 +1,9 @@
module RedmineDmsf module RedmineDmsf
module Webdav module Webdav
class BaseResource < DAV4Rack::Resource class BaseResource < DAV4Rack::Resource
include Redmine::I18n
include ActionView::Helpers::NumberHelper
DIR_FILE = "<tr><td class=\"name\"><a href=\"%s\">%s</a></td><td class=\"size\">%s</td><td class=\"type\">%s</td><td class=\"mtime\">%s</td></tr>" DIR_FILE = "<tr><td class=\"name\"><a href=\"%s\">%s</a></td><td class=\"size\">%s</td><td class=\"type\">%s</td><td class=\"mtime\">%s</td></tr>"
@ -24,7 +27,15 @@ module RedmineDmsf
@response.body = "" @response.body = ""
Confict unless collection? Confict unless collection?
entities = children.map{|child| DIR_FILE % [child.public_path.html_safe, child.long_name || child.name, "-", child.special_type || child.content_type, child.last_modified]} * "\n" entities = children.map{|child|
DIR_FILE % [
child.public_path,
child.long_name || child.name,
child.collection? ? '-' : number_to_human_size(child.content_length),
child.special_type || child.content_type,
child.last_modified
]
} * "\n"
@response.body << index_page % [ path.empty? ? "/" : path, path.empty? ? "/" : path , entities ] @response.body << index_page % [ path.empty? ? "/" : path, path.empty? ? "/" : path , entities ]
end end
@ -62,6 +73,12 @@ table { width:100%%; }
end end
protected protected
def basename
File.basename(path)
end
def dirname
File.dirname(path)
end
def Project def Project
return @Project unless @Project.nil? return @Project unless @Project.nil?
pinfo = @path.split('/').drop(1) pinfo = @path.split('/').drop(1)
@ -72,6 +89,9 @@ table { width:100%%; }
end end
end end
end end
def projectless_path
'/'+path.split('/').drop(2).join('/')
end
end end
end end
end end

View File

@ -0,0 +1,132 @@
module RedmineDmsf
module Webdav
class DmsfResource < BaseResource
def children
NotFound unless exist? && folder?
return @children unless @children.nil?
@children = []
@_folderdata.subfolders.map do |p|
@children.push child(p.title, p)
end
@_folderdata.files.map do |p|
@children.push child(p.name, p)
end
@children
end
def exist?
folder? || file?
end
def collection?
exist? && folder?
end
def folder?
return @_folder unless @_folder.nil?
@_folder = false
folders = DmsfFolder.find(:all, :conditions => ["project_id = :project_id", {:project_id => self.Project.id}], :order => "title ASC")
folders.delete_if {|x| x.title != basename}
return false unless folders.length > 0
if (folders.length > 1) then
folders.delete_if {|x| x.dmsf_path_str != projectless_path}
return false unless folders.length > 0
@_folder=true
@_folderdata = folders[0]
else
if ('/'+folders[0].dmsf_path_str == projectless_path) then
@_folder=true
@_folderdata = folders[0]
else
@_folder= false
end
end
@_folder
end
def file?
return @_file unless @_file.nil?
@_file = false
files = DmsfFile.find(:all, :conditions => ["project_id = :project_id AND name = :file_name AND deleted = :deleted", {:project_id => self.Project.id, :file_name => basename, :deleted => false}], :order => "name ASC")
files.delete_if {|x| File.dirname('/'+x.dmsf_path_str) != File.dirname(projectless_path)}
if files.length > 0
@_filedata = files[0]
@_file = true
end
end
def content_type
if folder? then
"inode/directory"
elsif file?
@_filedata.last_revision.detect_content_type
else
NotFound
end
end
def creation_date
if folder?
@_folderdata.created_at
elsif file?
@_filedata.created_at
else
NotFound
end
end
def last_modified
if folder?
@_folderdata.updated_at
elsif file?
@_filedata.updated_at
else
NotFound
end
end
def etag
filesize = file? ? @_filedata.size : 4096;
fileino = file? ? File.stat(@_filedata.last_revision.disk_file).ino : 2;
sprintf('%x-%x-%x', fileino, filesize, last_modified.to_i)
end
def content_length
file? ? @_filedata.size : 4096;
end
def special_type
l(:field_folder) if folder?
end
def get(request, response)
raise NotFound unless exist?
if collection?
html_display
response['Content-Length'] = response.body.bytesize.to_s
else
response.body = download
end
OK
end
protected
def download
raise NotFound unless file?
#log_activity("downloaded")
if @request.env['HTTP_RANGE'].nil?
access = DmsfFileRevisionAccess.new(:user_id => User.current.id, :dmsf_file_revision_id => @_filedata.last_revision.id,
:action => DmsfFileRevisionAccess::DownloadAction)
access.save!
end
Download.new(@_filedata.last_revision.disk_file)
end
end
end
end

View File

@ -0,0 +1,32 @@
require 'time'
require 'rack/utils'
require 'rack/mime'
module RedmineDmsf
module Webdav
class Download < Rack::File
def initialize(root, cache_control = nil)
@path = root
@cache_control = cache_control
end
def _call(env)
unless ALLOWED_VERBS.include? env["REQUEST_METHOD"]
return fail(405, "Method Not Allowed")
end
available = begin
F.file?(@path) && F.readable?(@path)
rescue SystemCallError
false
end
if available
serving(env)
else
raise NotFound
end
end
end
end
end

View File

@ -9,21 +9,18 @@ module RedmineDmsf
def children def children
#caching for repeat usage #caching for repeat usage
return @children unless @children.nil? return @children unless @children.nil?
@children = []
DmsfFolder.project_root_folders(self.Project).map do |p| DmsfFolder.project_root_folders(self.Project).map do |p|
child p.title, p @children.push child(p.title, p)
end end
DmsfFile.project_root_files(self.Project).map do |p| DmsfFile.project_root_files(self.Project).map do |p|
child p.display_name, p @children.push child(p.name, p)
end end
end @children
def name
self.Project.name unless self.Project.nil?
end end
def exist? def exist?
!self.Project.nil? !(self.Project.nil? || User.current.anonymous?)
end end
def collection? def collection?
@ -55,7 +52,7 @@ module RedmineDmsf
end end
def special_type def special_type
"Project" l(:field_project)
end end
def content_length def content_length