Add support for allprop and propname
This commit is contained in:
@@ -6,9 +6,9 @@ module Calligraphy
|
|||||||
# Resource responsible for writing and deleting directories and files to disk.
|
# Resource responsible for writing and deleting directories and files to disk.
|
||||||
class FileResource < Resource
|
class FileResource < Resource
|
||||||
DAV_PROPERTY_METHODS = %w[
|
DAV_PROPERTY_METHODS = %w[
|
||||||
creationdate displayname getcontentlanguage getcontentlength
|
allprop creationdate displayname getcontentlanguage getcontentlength
|
||||||
getcontenttype getetag getlastmodified lockdiscovery resourcetype
|
getcontenttype getetag getlastmodified lockdiscovery propname
|
||||||
supportedlock
|
resourcetype supportedlock
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
include Calligraphy::Utils
|
include Calligraphy::Utils
|
||||||
@@ -174,15 +174,7 @@ module Calligraphy
|
|||||||
def propfind(nodes)
|
def propfind(nodes)
|
||||||
properties = { found: [], not_found: [] }
|
properties = { found: [], not_found: [] }
|
||||||
|
|
||||||
nodes.each do |node|
|
find_properties_from_xml_elements nodes, properties
|
||||||
node.children.each do |prop|
|
|
||||||
next unless prop.is_a? Nokogiri::XML::Element
|
|
||||||
|
|
||||||
value = get_property prop
|
|
||||||
|
|
||||||
update_found_properties properties, prop, value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
properties[:found] = properties[:found].uniq.flatten if properties[:found]
|
properties[:found] = properties[:found].uniq.flatten if properties[:found]
|
||||||
properties
|
properties
|
||||||
@@ -507,6 +499,30 @@ module Calligraphy
|
|||||||
(lock_info[:check_creator] && (lock_info[:creator] == client_nonce))
|
(lock_info[:check_creator] && (lock_info[:creator] == client_nonce))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_properties_from_xml_elements(nodes, properties)
|
||||||
|
nodes.each do |node|
|
||||||
|
next unless node.is_a? Nokogiri::XML::Element
|
||||||
|
|
||||||
|
if node.children.length.positive?
|
||||||
|
find_properties_from_property_nodes node, properties
|
||||||
|
else
|
||||||
|
value = get_property node
|
||||||
|
|
||||||
|
update_found_properties properties, node, value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_properties_from_property_nodes(node, properties)
|
||||||
|
node.children.each do |prop|
|
||||||
|
next unless prop.is_a? Nokogiri::XML::Element
|
||||||
|
|
||||||
|
value = get_property prop
|
||||||
|
|
||||||
|
update_found_properties properties, prop, value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def ancestor_lock_tokens(lock_info)
|
def ancestor_lock_tokens(lock_info)
|
||||||
lock_info[:lock].each { |x| x }.map { |k| k[:locktoken].children[0].text }
|
lock_info[:lock].each { |x| x }.map { |k| k[:locktoken].children[0].text }
|
||||||
end
|
end
|
||||||
@@ -521,55 +537,94 @@ module Calligraphy
|
|||||||
def get_property(prop)
|
def get_property(prop)
|
||||||
case prop.name
|
case prop.name
|
||||||
when *DAV_PROPERTY_METHODS
|
when *DAV_PROPERTY_METHODS
|
||||||
prop.content = send prop.name
|
send prop.name, prop
|
||||||
prop
|
|
||||||
else
|
else
|
||||||
get_custom_property prop.name, deserialize: true
|
get_custom_property prop.name, deserialize: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def creationdate
|
def allprop(_prop)
|
||||||
@stats[:created_at]
|
get_custom_property nil, deserialize: true
|
||||||
|
|
||||||
|
{}.tap do |properties|
|
||||||
|
@store_properties.each_value do |node|
|
||||||
|
next unless node.is_a? Nokogiri::XML::Element
|
||||||
|
|
||||||
|
properties[node.name.to_sym] = node
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def displayname
|
def creationdate(prop)
|
||||||
get_custom_property(:displayname) || @name
|
prop.content = @stats[:created_at]
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def getcontentlanguage
|
def displayname(prop)
|
||||||
get_custom_property :contentlanguage
|
prop.content = get_custom_property(:displayname) || @name
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def getcontentlength
|
def getcontentlanguage(prop)
|
||||||
@stats[:size]
|
prop.content = get_custom_property :contentlanguage
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def getcontenttype
|
def getcontentlength(prop)
|
||||||
get_custom_property :contenttype
|
prop.content = @stats[:size]
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def getetag
|
def getcontenttype(prop)
|
||||||
|
prop.content = get_custom_property :contenttype
|
||||||
|
prop
|
||||||
|
end
|
||||||
|
|
||||||
|
def getetag(prop)
|
||||||
cache_key = ActiveSupport::Cache.expand_cache_key [@resource.etag, '']
|
cache_key = ActiveSupport::Cache.expand_cache_key [@resource.etag, '']
|
||||||
"W/\"#{Digest::MD5.hexdigest(cache_key)}\""
|
|
||||||
|
prop.content = "W/\"#{Digest::MD5.hexdigest(cache_key)}\""
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def getlastmodified
|
def getlastmodified(prop)
|
||||||
@updated_at
|
prop.content = @updated_at
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def lockdiscovery
|
# def include(prop)
|
||||||
fetch_lock_info
|
# # TODO: Implement
|
||||||
|
# prop
|
||||||
|
# end
|
||||||
|
|
||||||
|
def lockdiscovery(prop)
|
||||||
|
prop.content = fetch_lock_info
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def resourcetype
|
def propname(_prop)
|
||||||
'collection' if collection?
|
get_custom_property nil, deserialize: true
|
||||||
|
|
||||||
|
{}.tap do |properties|
|
||||||
|
@store_properties.each_value do |node|
|
||||||
|
next unless node.is_a? Nokogiri::XML::Element
|
||||||
|
|
||||||
|
properties[node.name.to_sym] = xml_node node.name
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def supportedlock
|
def resourcetype(prop)
|
||||||
|
prop.content = 'collection' if collection?
|
||||||
|
prop
|
||||||
|
end
|
||||||
|
|
||||||
|
def supportedlock(prop)
|
||||||
exclusive_write = lockentry_hash('exclusive', 'write')
|
exclusive_write = lockentry_hash('exclusive', 'write')
|
||||||
shared_write = lockentry_hash('shared', 'write')
|
shared_write = lockentry_hash('shared', 'write')
|
||||||
|
|
||||||
JSON.generate [exclusive_write, shared_write]
|
prop.content = JSON.generate [exclusive_write, shared_write]
|
||||||
|
prop
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_custom_property(prop, deserialize: false)
|
def get_custom_property(prop, deserialize: false)
|
||||||
@@ -609,11 +664,10 @@ module Calligraphy
|
|||||||
def add_properties(node, actions)
|
def add_properties(node, actions)
|
||||||
node.children.each do |prop|
|
node.children.each do |prop|
|
||||||
prop.children.each do |property|
|
prop.children.each do |property|
|
||||||
next unless node.is_a? Nokogiri::XML::Element
|
next unless property.is_a? Nokogiri::XML::Element
|
||||||
|
|
||||||
prop_sym = property.name.to_sym
|
prop_sym = property.name.to_sym
|
||||||
|
store_property_node property.clone.serialize, prop_sym
|
||||||
store_property_node property.serialize, prop_sym
|
|
||||||
|
|
||||||
actions[:set].push property
|
actions[:set].push property
|
||||||
end
|
end
|
||||||
|
|||||||
59
spec/requests/propfind_spec.rb
Normal file
59
spec/requests/propfind_spec.rb
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# frozen_string_literal: false
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'support/request_helpers'
|
||||||
|
require 'support/examples/propfind'
|
||||||
|
require 'support/examples/proppatch'
|
||||||
|
|
||||||
|
RSpec.describe 'PROPFIND', type: :request do
|
||||||
|
before(:all) do
|
||||||
|
tmp_dir = Rails.root.join('../../tmp').to_path
|
||||||
|
Dir.mkdir tmp_dir unless File.exists? tmp_dir
|
||||||
|
|
||||||
|
webdav_dir = Rails.root.join('../../tmp/webdav').to_path
|
||||||
|
FileUtils.rm_r webdav_dir if File.exists? webdav_dir
|
||||||
|
Dir.mkdir webdav_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
allow(Calligraphy).to receive(:enable_digest_authentication)
|
||||||
|
.and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with xml defintiion' do
|
||||||
|
before(:each) do
|
||||||
|
put '/webdav/bar.html', headers: {
|
||||||
|
RAW_POST_DATA: 'hello world'
|
||||||
|
}
|
||||||
|
proppatch '/webdav/bar.html', headers: {
|
||||||
|
RAW_POST_DATA: Support::Examples::Proppatch.rfc4918_9_2_2
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'allprop' do
|
||||||
|
it 'returns all property names and values' do
|
||||||
|
propfind '/webdav/bar.html', headers: {
|
||||||
|
RAW_POST_DATA: Support::Examples::Propfind.allprop
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(207)
|
||||||
|
expect(response.body).to include('Authors')
|
||||||
|
expect(response.body).to include('Author>')
|
||||||
|
expect(response.body).to include('Jim')
|
||||||
|
expect(response.body).to include('Roy')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'propname' do
|
||||||
|
it 'returns all property names' do
|
||||||
|
propfind '/webdav/bar.html', headers: {
|
||||||
|
RAW_POST_DATA: Support::Examples::Propfind.propname
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(207)
|
||||||
|
expect(response.body).to include('Authors/')
|
||||||
|
expect(response.body).to_not include('Author/')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
25
spec/support/examples/propfind.rb
Normal file
25
spec/support/examples/propfind.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# frozen_string_literal: false
|
||||||
|
|
||||||
|
module Support
|
||||||
|
module Examples
|
||||||
|
module Propfind
|
||||||
|
def self.allprop
|
||||||
|
<<~XML
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<D:propfind xmlns:D="DAV:">
|
||||||
|
<D:allprop/>
|
||||||
|
</D:propfind>
|
||||||
|
XML
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.propname
|
||||||
|
<<~XML
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<D:propfind xmlns:D="DAV:">
|
||||||
|
<D:propname/>
|
||||||
|
</D:propfind>
|
||||||
|
XML
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
28
spec/support/examples/proppatch.rb
Normal file
28
spec/support/examples/proppatch.rb
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# frozen_string_literal: false
|
||||||
|
|
||||||
|
module Support
|
||||||
|
module Examples
|
||||||
|
module Proppatch
|
||||||
|
# RFC4918: 9.2.2
|
||||||
|
def self.rfc4918_9_2_2
|
||||||
|
<<~XML
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<D:propertyupdate xmlns:D="DAV:"
|
||||||
|
xmlns:Z="http://ns.example.com/standards/z39.50/">
|
||||||
|
<D:set>
|
||||||
|
<D:prop>
|
||||||
|
<Z:Authors>
|
||||||
|
<Z:Author>Jim Whitehead</Z:Author>
|
||||||
|
<Z:Author>Roy Fielding</Z:Author>
|
||||||
|
</Z:Authors>
|
||||||
|
</D:prop>
|
||||||
|
</D:set>
|
||||||
|
<D:remove>
|
||||||
|
<D:prop><Z:Copyright-Owner/></D:prop>
|
||||||
|
</D:remove>
|
||||||
|
</D:propertyupdate>
|
||||||
|
XML
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user