commit
2f9fc02114
@ -60,8 +60,8 @@ module RedmineDmsf
|
||||
@public_path.force_encoding('utf-8')
|
||||
end
|
||||
|
||||
# Generate HTML for Get requests
|
||||
def html_display
|
||||
# Generate HTML for Get requests, or Head requests if no_body is true
|
||||
def html_display(no_body = false)
|
||||
@response.body = ''
|
||||
Confict unless collection?
|
||||
entities = children.map{|child|
|
||||
@ -80,7 +80,7 @@ module RedmineDmsf
|
||||
'',
|
||||
'',
|
||||
] + entities if parent
|
||||
@response.body << index_page % [ path.empty? ? '/' : path, path.empty? ? '/' : path, entities ]
|
||||
@response.body << index_page % [ path.empty? ? '/' : path, path.empty? ? '/' : path, entities ] unless no_body
|
||||
end
|
||||
|
||||
# Run method through proxy class - ensuring always compatible child is generated
|
||||
|
||||
@ -27,6 +27,36 @@ module RedmineDmsf
|
||||
class Controller < DAV4Rack::Controller
|
||||
include DAV4Rack::Utils
|
||||
|
||||
# Return response to OPTIONS
|
||||
def options
|
||||
# exist? returns false if user is anonymous for ProjectResource and DmsfResource, but not for IndexResource.
|
||||
unless(resource.exist? || (User.current && User.current.anonymous?))
|
||||
# Return NotFound if resource does not exist and the request is not anonymous.
|
||||
NotFound
|
||||
else
|
||||
if request.env.has_key?('HTTP_X_OFFICE_MAJOR_VERSION') && User.current && User.current.anonymous?
|
||||
# Anonymous request from MsOffice, respond 405.
|
||||
# If responding with 401 then MsOffice will fail.
|
||||
# If responding with 200 then MsOffice will think that anonymous access is ok for everything.
|
||||
# Responding with 405 is a workaround found in https://support.microsoft.com/en-us/kb/2019105
|
||||
MethodNotAllowed
|
||||
else
|
||||
resource.options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return response to HEAD
|
||||
def head
|
||||
# exist? returns false if user is anonymous for ProjectResource and DmsfResource, but not for IndexResource.
|
||||
unless(resource.exist? || (request.env.has_key?('HTTP_X_OFFICE_MAJOR_VERSION') && User.current && User.current.anonymous?))
|
||||
# Return NotFound if resource does not exist and the request is not from an anonymous MsOffice product.
|
||||
NotFound
|
||||
else
|
||||
resource.head(request, response)
|
||||
end
|
||||
end
|
||||
|
||||
# Return response to PROPFIND
|
||||
def propfind
|
||||
unless(resource.exist?)
|
||||
|
||||
@ -53,7 +53,7 @@ module RedmineDmsf
|
||||
# Our already quite heavy usage of DB would just get silly every time we called
|
||||
# this method.
|
||||
def children
|
||||
unless @childern
|
||||
unless @children
|
||||
@children = []
|
||||
if collection?
|
||||
folder.dmsf_folders.select(:title).visible.map do |p|
|
||||
@ -195,6 +195,23 @@ module RedmineDmsf
|
||||
OK
|
||||
end
|
||||
|
||||
# Process incoming HEAD request
|
||||
#
|
||||
# MsOFfice uses anonymous HEAD requests, so always return a response.
|
||||
# See https://support.microsoft.com/en-us/kb/2019105
|
||||
##
|
||||
def head(request, response)
|
||||
raise NotFound unless project && project.module_enabled?('dmsf') && (folder || file)
|
||||
|
||||
if collection?
|
||||
html_display(true)
|
||||
response['Content-Length'] = response.body.bytesize.to_s
|
||||
else
|
||||
response.body = ''
|
||||
end
|
||||
OK
|
||||
end
|
||||
|
||||
# Process incoming MKCOL request
|
||||
#
|
||||
# Create a DmsfFolder at location requested, only if parent is a folder (or root)
|
||||
@ -227,7 +244,19 @@ module RedmineDmsf
|
||||
def delete
|
||||
if file
|
||||
raise Forbidden unless User.current.admin? || User.current.allowed_to?(:file_delete, project)
|
||||
file.delete(false) ? NoContent : Conflict
|
||||
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)
|
||||
destroy = true
|
||||
elsif file.last_revision.size == 0
|
||||
# Zero-sized files should be destroyed
|
||||
destroy = true
|
||||
else
|
||||
destroy = false
|
||||
end
|
||||
file.delete(destroy) ? NoContent : Conflict
|
||||
elsif folder
|
||||
raise Forbidden unless User.current.admin? || User.current.allowed_to?(:folder_manipulation, project)
|
||||
folder.delete(false) ? NoContent : Conflict
|
||||
@ -298,8 +327,8 @@ module RedmineDmsf
|
||||
# Save
|
||||
new_revision.save && resource.file.save
|
||||
|
||||
# Delete the file that should have been renamed.
|
||||
file.delete(false) ? NoContent : Conflict
|
||||
# Delete (and destroy) the file that should have been renamed and return what should have been returned in case of a copy
|
||||
file.delete(true) ? Created : PreconditionFailed
|
||||
else
|
||||
# Files cannot be merged at this point, until a decision is made on how to merge them
|
||||
# ideally, we would merge revision history for both, ensuring the origin file wins with latest revision.
|
||||
@ -526,6 +555,7 @@ module RedmineDmsf
|
||||
end
|
||||
|
||||
new_revision = DmsfFileRevision.new
|
||||
reuse_revision = false
|
||||
|
||||
if exist? # We're over-writing something, so ultimately a new revision
|
||||
f = file
|
||||
@ -533,6 +563,7 @@ module RedmineDmsf
|
||||
if last_revision.size == 0
|
||||
new_revision = last_revision
|
||||
new_revision.minor_version -= 1
|
||||
reuse_revision = true
|
||||
else
|
||||
new_revision.source_revision = last_revision
|
||||
if last_revision
|
||||
@ -574,7 +605,7 @@ module RedmineDmsf
|
||||
|
||||
raise InternalServerError unless new_revision.valid? && f.save
|
||||
|
||||
new_revision.disk_filename = new_revision.new_storage_filename
|
||||
new_revision.disk_filename = new_revision.new_storage_filename unless reuse_revision
|
||||
|
||||
if new_revision.save
|
||||
new_revision.copy_file_content(request.body)
|
||||
@ -610,6 +641,13 @@ module RedmineDmsf
|
||||
end
|
||||
end
|
||||
|
||||
def options_req
|
||||
response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
|
||||
response["Dav"] = '1, 2'
|
||||
response["Ms-Author-Via"] = "DAV"
|
||||
OK
|
||||
end
|
||||
|
||||
private
|
||||
# Prepare file for download using Rack functionality:
|
||||
# Download (see RedmineDmsf::Webdav::Download) extends Rack::File to allow single-file
|
||||
|
||||
@ -69,12 +69,26 @@ module RedmineDmsf
|
||||
4096
|
||||
end
|
||||
|
||||
def head(request, response)
|
||||
html_display(true)
|
||||
response['Content-Length'] = response.body.bytesize.to_s
|
||||
OK
|
||||
end
|
||||
|
||||
def get(request, response)
|
||||
html_display
|
||||
response['Content-Length'] = response.body.bytesize.to_s
|
||||
OK
|
||||
end
|
||||
|
||||
def options_req
|
||||
response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
|
||||
#response["Allow"] = 'OPTIONS,PROPFIND'
|
||||
response["Dav"] = '1, 2'
|
||||
response["Ms-Author-Via"] = "DAV"
|
||||
OK
|
||||
end
|
||||
|
||||
# Bugfix: Ensure that this level never indicates a parent
|
||||
def parent
|
||||
nil
|
||||
|
||||
@ -85,12 +85,30 @@ module RedmineDmsf
|
||||
4096
|
||||
end
|
||||
|
||||
def head(request, response)
|
||||
# HEAD must be allowed even for anonymous users, so just verify that the project exists and that the dmsf module is enabled.
|
||||
if project.nil? || !project.module_enabled?('dmsf')
|
||||
NotFound
|
||||
else
|
||||
html_display(true)
|
||||
response['Content-Length'] = response.body.bytesize.to_s
|
||||
OK
|
||||
end
|
||||
end
|
||||
|
||||
def get(request, response)
|
||||
html_display
|
||||
response['Content-Length'] = response.body.bytesize.to_s
|
||||
OK
|
||||
end
|
||||
|
||||
def options_req
|
||||
response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
|
||||
response["Dav"] = '1, 2'
|
||||
response["Ms-Author-Via"] = "DAV"
|
||||
OK
|
||||
end
|
||||
|
||||
def folder
|
||||
nil
|
||||
end
|
||||
@ -100,4 +118,4 @@ module RedmineDmsf
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -51,7 +51,13 @@ module RedmineDmsf
|
||||
# going to fork it to ensure compliance, checking the request method in the authentication
|
||||
# seems the next best step, if the request method is OPTIONS return true, controller will simply
|
||||
# call the options method within, which accesses nothing, just returns headers about dav env.
|
||||
return true if @request.request_method.downcase == 'options' && (path == '/' || path.empty?)
|
||||
#return true if @request.request_method.downcase == 'options' && (path == '/' || path.empty?)
|
||||
|
||||
# Allow anonymous OPTIONS requests.
|
||||
return true if @request.request_method.downcase == 'options'
|
||||
# Allow anonymous HEAD requests.
|
||||
return true if @request.request_method.downcase == 'head'
|
||||
|
||||
return false unless username && password
|
||||
User.current = User.try_to_login(username, password)
|
||||
return User.current && !User.current.anonymous?
|
||||
@ -93,6 +99,10 @@ module RedmineDmsf
|
||||
@resource_c.content_length
|
||||
end
|
||||
|
||||
def head(request,response)
|
||||
@resource_c.head(request, response)
|
||||
end
|
||||
|
||||
def get(request, response)
|
||||
@resource_c.get(request, response)
|
||||
end
|
||||
@ -153,6 +163,9 @@ module RedmineDmsf
|
||||
@resource_c.properties
|
||||
end
|
||||
|
||||
def options
|
||||
@resource_c.options_req
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user