railsdav 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # RailsDAV
2
+
3
+ Make your Rails 3 resources accessible via WebDAV
4
+
5
+ ProvidesThis gem provides basic Rails 3 extensions for making your business resources accessible via WebDAV. This gem does by no means by no means implement the full WebDAV semantics, but it suffices to access your app with client(-libs) such as Konqueror, cadaver, davfs2 or NetDrive.
6
+
7
+ ## Compatibility
8
+
9
+ Definitely works with Rails 3.0.9 and 3.2.13, but should also work with versions in between.
10
+ This is due to some hacking done "under the hood" in the Rails routing implementation as well as a simple method override in ActionController.
11
+
12
+ f you encounter any problems with other Rails versions, please feel free to report an issue or send me a pull request on github.
13
+
14
+ ## Installation
15
+
16
+ Just at the following to your Gemfile
17
+
18
+ gem 'railsdav', :git => 'https://github.com/wvk/railsdav.git'
19
+
20
+ and then run
21
+
22
+ bundle install
23
+
24
+
25
+ ## Usage
26
+
27
+ TODO: Context
28
+
29
+ ### Controller
30
+
31
+ #### Doing the Set-Up
32
+
33
+ Tell your controller which actions should be available as WebDAV resources, what media types should be advertised and whether or not they should be also available as collection resources (i.e. "folders"):
34
+
35
+ class FoosController < ApplicationController
36
+ enable_webdav_for :index,
37
+ :accept => :json
38
+
39
+ enable_webdav_for :show,
40
+ :accept => [:xml, :json],
41
+ :collection => false
42
+
43
+ # ...
44
+
45
+ This stores some metadata about your actions that can be accessed from another controller, in case this one acts as a subresource of another resource. You may provide an arbitrary list of media types ("formats") with the :accept options. This causes RailsDAV to advertise that resource as separate files, one for each provided media type.
46
+ By default, RailsDAV also advertises each resource as a collection ("folder"), containing a resource's subresources. You may choose not to let it do so by setting the :collection option to false (nil won't do, it really must be /false/).
47
+
48
+ # Handling PROPFIND Requests
49
+
50
+ As for the actions themselves they, too, need some attention. Collection resources usually contain subresources (otherwise, why bother?) that are specified in the format.webdav responder block:
51
+
52
+ # ...
53
+
54
+ def index
55
+ @foos = Foo.limit(100)
56
+ respond_to do |format|
57
+ # you already know this...
58
+ format.html
59
+ format.xml { render :json => @foos }
60
+ format.json { render :xml => @foos }
61
+
62
+ # and this one is new...
63
+ format.webdav do |dav|
64
+ @foos.each |foo|
65
+ dav.subresource foo_path(foo)
66
+ end
67
+ dav.format :xml, :size => @foos.to_json.size, :updated_at => @foos.maximum(:updated_at)
68
+ dav.format :json, :size => @foos.to_xml.size, :updated_at => @foos.maximum(:updated_at)
69
+ end
70
+ end
71
+ end
72
+
73
+ # ...
74
+
75
+ The webdav responder block is called when a PROPFIND request comes in. Within the block, we define a metadata set for each possible mime type that will be served via WebDAV. If the index resource is accessed as a collection (PROPFIND /foos), a multi-status response is rendered with the foos collection containting up to 100 foo entries as separate XML and JSON files:
76
+
77
+ PROPFIND /foos
78
+
79
+ will return a directory layout similar to:
80
+
81
+ /foos
82
+ -> /foos/1.xml
83
+ -> /foos/1.json
84
+ -> /foos/2.xml
85
+ -> /foos/2.json
86
+ ...
87
+
88
+ If the index resource is accessed as XML (PROPFIND /foos.xml) or JSON (PROPFIND /foos.json), size and last-update of the XML/JSON "file" are included in the response. You can include that metatdata for each subresource, too:
89
+
90
+ format.webdav do |dav|
91
+ @foos.each do |foo|
92
+ dav.subresource foo_path(foo), :updated_at => foo.updated_at
93
+ end
94
+ end
95
+
96
+ the "dav.format" statements are entirely optional if you do not wish to include any metadata such as updated-at, created-at, or size. There's no need to be redundant here, since you already specified the most important metadata with "enable_webdav_for".
97
+
98
+ The show action looks similar.
99
+
100
+ def show
101
+ @foo = Foo.find(params[:id])
102
+ respond_to do |format|
103
+ format.html
104
+ format.json { render :json => @foo }
105
+ format.xml { render :xml => @foo }
106
+
107
+ format.webdav do |dav|
108
+ dav.format :xml, :size => @foo.to_xml.size, :updated_at => @foo.updated_at
109
+ dav.format :json, :size => @foo.to_json.size, :updated_at => @foo.updated_at
110
+ end
111
+ end
112
+ end
113
+
114
+ TODO: explain why respond_to is always needed
115
+
116
+ TODO: explain difference between PROPFIND /foos/1 and PROPFIND /foos/1.:format and medadata defined on class level and in responder block
117
+
118
+ ### Routing
119
+
120
+ To make the rails routing mechanism webdav aware, RailsDAV extends it by means of some new DSL methods. Let's take the above example again and what the default routing would look like:
121
+
122
+ MyAwesomeApp::Application.routes.draw do
123
+ resources :foos
124
+ end
125
+
126
+ In order to accept PROPFIND and other WebDAV requests, you'll have to change it to look rather this way:
127
+
128
+ MyAwesomeApp::Application.routes.draw do
129
+ webdav_resources :foos
130
+ end
131
+
132
+ Easy enough. As you may have guessed, there exists also a "webdav_resource" counterpart for singleton resources and some more. By the way: the above also produces a route for OPTIONS requests that matches the capabilities of the resource. In case you only want the show and update action to be present, the following would also adapt the OPTIONS responder to only contain PROPFIND, GET and PUT:
133
+
134
+ webdav_resource :bar,
135
+ :only => %w(show update)
136
+
137
+ To enable propfind for a single action somewhere, use:
138
+ dav_propfind '/my-foo', :to => 'my_foos#show'
139
+
140
+ the other available helpers are: dav_copy, dav_move, dav_mkcol, dav_lock, dav_unlock, dav_proppatch.
141
+
142
+ ### Authentication
143
+
144
+ RailsDAV does not do any authentication whatsoever, nor is there any sugar to go nicely with $your_favourite_authentication_gem. However, since cookie/session based authentication does not like to be friends with WebDAV, it's up to you to ensure Basic or Digest Authentication is used when a request from a WebDAV client comes in.
145
+
146
+ Assuming you have an application where resources are normally accessed as text/html but never so for WebDAV, a simple means of providing access control using HTTP Basic might look like this:
147
+
148
+ class ApplicationController < ActionController::Base
149
+ before_filter :authenticate_unless_session
150
+
151
+ protected
152
+
153
+ def authenticate_unless_session
154
+ # Always use Basic Authentication if the request method is one of WebDAV's
155
+ if is_webdav_request?
156
+ basic_auth
157
+ elsif request.format == Mime::HTML
158
+ # skip Basic Authentication and use anoher way
159
+ else
160
+ basic_auth # or whatever...
161
+ end
162
+ end
163
+
164
+ def basic_auth
165
+ session = authenticate_with_http_basic {|usr, pwd| Session.new :login => usr, :password => pwd }
166
+ if session and session.valid?
167
+ @current_user = session.user
168
+ else
169
+ request_http_basic_authentication 'My Awesome App'
170
+ end
171
+ end
172
+ end
173
+
174
+ is_webdav_request? checks whether an Incoming request is issued by a WebDAV client using any specific HTTP verbs (such as PROPFIND, MKCOL, COPY, MOVE, etc.) or a media type other than text/html is requested.
175
+
176
+ ## Changelog
177
+
178
+ * 0.0.5: Rails 3.1.x compatibility, add encoding hints, fix ResourceDescriptor load error
179
+ * 0.0.4: Basic support for allprop in PROPFIND
180
+ * 0.0.3: Change the API within the responder block to a more concise one
181
+ * 0.0.2: More or less a complete rewrite: Use more sensible API, modularize the renderer code, get rid of controller monkey patching
182
+ * 0.0.1: Initial Release: Basic support for PROPFIND and webdav_resource(s) based routing
183
+
184
+ Copyright (c) 2012 Willem van Kerkhof <wvk@consolving.de>, released under the MIT license
185
+
data/init.rb ADDED
File without changes
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ module Railsdav
4
+ module ControllerExtensions
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :webdav_metadata
9
+
10
+ alias_method_chain :respond_to, :webdav
11
+ alias_method_chain :respond_with, :webdav
12
+ end
13
+
14
+ module ClassMethods
15
+ def enable_webdav_for(*names_and_options, &block)
16
+ options = names_and_options.extract_options!
17
+ names = names_and_options
18
+ self.webdav_metadata ||= {}
19
+
20
+ options[:collection] = true unless options.has_key?(:collection)
21
+
22
+ names.each do |name|
23
+ self.webdav_metadata[name] = options
24
+ end
25
+ end
26
+
27
+ def webdav_metadata_for_action(action)
28
+ webdav_metadata[action.to_sym]
29
+ end
30
+ end
31
+
32
+ # decorate behaviour defined in ActionController::MimeResponds
33
+ def respond_to_with_webdav(*mimes, &block)
34
+ if request.propfind?
35
+ render :webdav => :propstat, :respond_to_block => block
36
+ else
37
+ respond_to_without_webdav *mimes, &block
38
+ end
39
+ end
40
+
41
+ # decorate behaviour defined in ActionController::MimeResponds
42
+ def respond_with_with_webdav(*resources, &block)
43
+ if request.propfind?
44
+ render :webdav => :propstat, :respond_to_block => block
45
+ else
46
+ respond_with_without_webdav *resources, &block
47
+ end
48
+ end
49
+
50
+ def webdav_metadata_for_current_action
51
+ self.class.webdav_metadata_for_action params[:action]
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module Railsdav
4
+ class Renderer
5
+ class ResponseCollector
6
+ attr_writer :controller
7
+
8
+ delegate :subresources,
9
+ :to => :@selector
10
+
11
+ def initialize(controller, request_format)
12
+ @controller, @request_format = controller, request_format
13
+ @selector = ResponseTypeSelector.new(controller, request_format)
14
+ end
15
+
16
+ def resource
17
+ @resource ||= Renderer::ResourceDescriptor.new(@controller.request.url, @selector.resource_options)
18
+ end
19
+
20
+ # responds to calls like html, xml, json by ignoring them
21
+ def method_missing(name, *args)
22
+ super unless Mime::EXTENSION_LOOKUP[name.to_s]
23
+ end
24
+
25
+ def webdav
26
+ if block_given?
27
+ yield @selector
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ module Railsdav
4
+ class Renderer
5
+ class ResponseTypeSelector
6
+ attr_reader :subresources, :resource_options
7
+
8
+ def initialize(controller, request_format)
9
+ @controller, @request_format = controller, request_format
10
+ @subresources = []
11
+
12
+ # TODO: somehow allow passing more options to current resource
13
+ @resource_options = {:format => @request_format.to_sym}
14
+ end
15
+
16
+ def format(name, options)
17
+ if Mime::EXTENSION_LOOKUP[name.to_s]
18
+ if @request_format.to_sym == name
19
+ # TODO: somehow get the attributes (size, updated-at, ...) from the actual Mime responder block here
20
+ @resource_options.merge! options
21
+ end
22
+ else
23
+ raise UnknownMimeTypeExtension, "#{name} is not a valid MIME type file extension."
24
+ end
25
+ end
26
+
27
+ # loop over all urls marked as subresources in webdav responder block
28
+ # and add them with all their acceptable mime types
29
+ def subresource(*subresources)
30
+ return unless @request_format == :collection
31
+
32
+ options = subresources.extract_options!
33
+ subresources.each do |resource_url|
34
+ route = Rails.application.routes.recognize_path(resource_url)
35
+
36
+ if meta = Renderer.webdav_metadata_for_url(route)
37
+ # show the resource as a collection unless disabled
38
+ if meta[:collection]
39
+ @subresources << Renderer::ResourceDescriptor.new(resource_url, options.merge(:format => :collection))
40
+ elsif meta[:accept].blank?
41
+ # show the resource without .:format suffix, but not as a collection
42
+ @subresources << Renderer::ResourceDescriptor.new(resource_url, options)
43
+ end
44
+
45
+ # show the resource with all the specified format suffixes
46
+ if meta[:accept]
47
+ [meta[:accept]].flatten.each do |type_name|
48
+ mime_type = Mime::Type.lookup_by_extension(type_name)
49
+ subresource_url = @controller.url_for(route.merge(:format => type_name))
50
+ @subresources << Renderer::ResourceDescriptor.new(subresource_url, options.merge(:format => mime_type))
51
+ end
52
+ end
53
+ else
54
+ raise MissingWebDAVMetadata, "no WebDAV metadata found for #{resource_url}, please specify it using #enable_webdav_for"
55
+ end
56
+ end
57
+
58
+ @resource_options[:size] = @subresources.size
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,191 @@
1
+ # encoding: utf-8
2
+
3
+ require 'builder'
4
+
5
+ module Railsdav
6
+ class Renderer
7
+ autoload :ResourceDescriptor, 'railsdav/renderer/resource_descriptor'
8
+ autoload :ResponseCollector, 'railsdav/renderer/response_collector'
9
+ autoload :ResponseTypeSelector, 'railsdav/renderer/response_type_selector'
10
+
11
+ # Return the webdav metadata for a given URL/path
12
+ #
13
+ # If the given route is recognized, a controller and action can be identified.
14
+ # To get the properties for that URI, we lookup the metadata
15
+ # specified in the target controller (see #enable_webdav_for)
16
+ #
17
+ def self.webdav_metadata_for_url(routing_data)
18
+ controller = "#{routing_data[:controller]}_controller".camelize.constantize
19
+ controller ? controller.webdav_metadata_for_action(routing_data[:action]) : nil
20
+ end
21
+
22
+ attr_accessor :depth
23
+
24
+ # Create a new Renderer instance that will render an XML multistatus response.
25
+ #
26
+ # Arguments:
27
+ # controller: the current controller instance
28
+ #
29
+ def initialize(controller)
30
+ @controller = controller
31
+ @request = controller.request
32
+ @depth = (@request.headers['Depth'].to_i > 0) ? 1 : 0 # depth "infinite" is not yet supported
33
+ end
34
+
35
+ # Render the requested response_type.
36
+ #
37
+ # Arguments:
38
+ # response_type: currently either :propstat or :response.
39
+ # options:
40
+ # - for response: :href, :status, :error (see #response)
41
+ # - for propstat: :size, :format, :created_at, :updated_at, :resource_layout, ... (see #propstat)
42
+ def respond_with(response_type, options = {})
43
+ self.send response_type, options
44
+ end
45
+
46
+ protected
47
+
48
+ def request_format
49
+ if @controller.params[:format].blank?
50
+ if @controller.webdav_metadata_for_current_action[:collection]
51
+ return :collection
52
+ else
53
+ return @controller.request_format
54
+ end
55
+ else
56
+ return @controller.params[:format]
57
+ end
58
+ end
59
+
60
+ def render
61
+ @dav = Builder::XmlMarkup.new :indent => 2
62
+ @dav.instruct!
63
+ @dav.multistatus :xmlns => 'DAV:' do
64
+ yield @dav
65
+ end
66
+ @dav.target!
67
+ end
68
+
69
+ # Render a WebDAV multistatus response with a single "response" element.
70
+ # This is primarily intended vor responding to single resource errors.
71
+ #
72
+ # Arguments:
73
+ # options:
74
+ # - href: the requested resource URL, usually request.url
75
+ # - status: the response status, something like :unprocessable_entity or 204.
76
+ # - error: an Error description, if any.
77
+ #
78
+ def response(options = {})
79
+ elements = options.slice(:error)
80
+
81
+ render do
82
+ response_for options[:href] do |dav|
83
+ elements.each do |name, value|
84
+ status_for options[:status]
85
+ dav.__send__ name, value
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def status_for(status)
92
+ code = Rack::Utils.status_code(status || :ok)
93
+ status = "HTTP/1.1 #{code} #{Rack::Utils::HTTP_STATUS_CODES[code]}"
94
+ @dav.status status
95
+ end
96
+
97
+ # Render a WebDAV multistatus response with a "response" element per resource
98
+ # TODO: explain the magic implemented here
99
+ # Arguments:
100
+ # TODO: describe valid arguments
101
+ #
102
+ def propstat(options = {})
103
+ response_collector = ResponseCollector.new(@controller, self.request_format)
104
+ # retrieve properties for this resource and all subresources if this is a collection
105
+ if options[:respond_to_block]
106
+ options[:respond_to_block].call(response_collector)
107
+ end
108
+
109
+ render do |dav|
110
+ propstat_for response_collector.resource
111
+ propstat_for response_collector.subresources if @depth > 0
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def propstat_for(*resources)
118
+ params = @controller.params
119
+ params[:propfind] ||= {:prop => []}
120
+
121
+ if params[:propfind].has_key? 'allprop'
122
+ requested_properties = nil # fill it later, see below.
123
+ else
124
+ requested_properties = params[:propfind][:prop]
125
+ end
126
+
127
+ resources.flatten.each do |resource|
128
+ hash = resource.props
129
+
130
+ case hash[:updated_at]
131
+ when Time, DateTime
132
+ updated_at = hash[:updated_at]
133
+ when String
134
+ updated_at = Time.parse(hash[:updated_at])
135
+ else
136
+ updated_at = Time.now
137
+ end
138
+
139
+ response_hash = {
140
+ :quota_used_bytes => 0,
141
+ :quota_available_bytes => 10.gigabytes,
142
+ :creationdate => updated_at.rfc2822,
143
+ :getlastmodified => updated_at.rfc2822,
144
+ :getcontentlength => hash[:size],
145
+ :getcontenttype => hash[:format].to_s
146
+ }
147
+
148
+ if resource.collection?
149
+ response_hash[:resourcetype] = proc { @dav.tag! :collection } # must be block to render <collection></collection> instead of "collection"
150
+ response_hash[:getcontenttype] = nil
151
+ end
152
+
153
+ requested_properties ||= response_hash.keys
154
+
155
+ response_for(resource.url) do |dav|
156
+ dav.propstat do
157
+ status_for hash[:status]
158
+ dav.prop do
159
+ requested_properties.each do |prop_name, opts|
160
+ if prop_val = response_hash[prop_name.to_sym]
161
+ if prop_val.respond_to? :call
162
+ if opts
163
+ dav.tag! prop_name, opts, &prop_val
164
+ else
165
+ dav.tag! prop_name, &prop_val
166
+ end
167
+ else
168
+ dav.tag! prop_name, prop_val, opts
169
+ end
170
+ elsif opts
171
+ dav.tag! prop_name, opts
172
+ else
173
+ dav.tag! prop_name
174
+ end
175
+ end # requested_properties.each
176
+ end # dav.prop
177
+ end # dav.propstat
178
+ end # dav.response_for
179
+ end # resource_layout.keys.each
180
+ end # def propstat_for
181
+
182
+ def response_for(href)
183
+ @dav.response do
184
+ @dav.href href
185
+ yield @dav
186
+ end
187
+ end
188
+
189
+ end # class Railsdav
190
+ end # module Railsdav
191
+
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Railsdav
4
+ module RequestExtensions
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ Railsdav::WEBDAV_HTTP_VERBS.each do |verb|
9
+ method_name = "#{verb.underscore}?"
10
+
11
+ # just to make sure we don't accidentally break things...
12
+ raise "#{method_name} is already defined in #{self.class}!" if respond_to? method_name
13
+
14
+ define_method method_name do
15
+ ActionDispatch::Request::HTTP_METHOD_LOOKUP[request_method] == verb.underscore.to_sym
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def webdav?
22
+ Railsdav::WEBDAV_HTTP_VERBS.include? request_method
23
+ end
24
+
25
+ end # module RequestExtensions
26
+ end # module Railsdav
@@ -0,0 +1,213 @@
1
+ # encoding: utf-8
2
+
3
+ # Allow usage of WebDAV specific HTTP verbs
4
+ # userinfo is not a standard webdav verb, bur davfs2 uses it
5
+ # and we want to be prepared to ignore it gracefully (e.g.
6
+ # by sending a :not_implemented response)
7
+ verbs = %w(propfind proppatch mkcol copy move lock unlock userinfo)
8
+ verbs.each do |method|
9
+ ActionDispatch::Request::HTTP_METHODS << method.upcase
10
+ ActionDispatch::Request::HTTP_METHOD_LOOKUP[method.upcase] = method.to_sym
11
+ end
12
+
13
+ # Extend routing s.t. webdav_resource and webdav_resources can be used,
14
+ # enabling things like PROPFIND /foo/index.(:format) and such.
15
+ #
16
+ # ATTENTION: adapt this to newer rails version if upgrading the framework!
17
+ class ActionDispatch::Routing::Mapper
18
+ module HttpHelpers
19
+ def dav_propfind(*args, &block)
20
+ map_method(:propfind, *args, &block)
21
+ end
22
+
23
+ def dav_options(*args, &block)
24
+ map_method(:options, *args, &block)
25
+ end
26
+
27
+ def dav_copy(*args, &block)
28
+ map_method(:copy, *args, &block)
29
+ end
30
+
31
+ def dav_move(*args, &block)
32
+ map_method(:move, *args, &block)
33
+ end
34
+
35
+ def dav_mkcol(*args, &block)
36
+ map_method(:mkcol, *args, &block)
37
+ end
38
+
39
+ def dav_lock(*args, &block)
40
+ map_method(:lock, *args, &block)
41
+ end
42
+
43
+ def dav_unlock(*args, &block)
44
+ map_method(:unlock, *args, &block)
45
+ end
46
+
47
+ def dav_proppatch(*args, &block)
48
+ map_method(:proppatch, *args, &block)
49
+ end
50
+ end
51
+
52
+ module Resources
53
+ CANONICAL_ACTIONS << :update_all
54
+
55
+ class WebDAVResource < Resource
56
+ DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit, :update_all]
57
+ end
58
+
59
+ class WebDAVSingletonResource < SingletonResource
60
+ DEFAULT_ACTIONS = [:show, :create, :update, :destroy, :new, :edit]
61
+ end
62
+
63
+ def resource_scope?
64
+ [:webdav_resource, :webdav_resources, :resource, :resources].include?(@scope[:scope_level])
65
+ end
66
+
67
+ if Rails.version <= '3.1.0'
68
+ # Rails versions after 3.1 expect two arguments here, the first being :resource, :resources,
69
+ # :webdav_resource etc.so we don't need the inferring logic anymore in newer versions.
70
+ def resource_scope(resource)
71
+ case resource
72
+ when WebDAVSingletonResource
73
+ scope_level = :webdav_resource
74
+ when WebDAVResource
75
+ scope_level = :webdav_resources
76
+ when SingletonResource
77
+ scope_level = :resource
78
+ when Resource
79
+ scope_level = :resources
80
+ end
81
+ with_scope_level(scope_level, resource) do
82
+ scope(parent_resource.resource_scope) do
83
+ yield
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def dav_options_response(*allowed_http_verbs)
90
+ lambda { [200, {'Allow' => allowed_http_verbs.flatten.map{|s| s.to_s.upcase}.join(' '), 'DAV' => '1'}, ''] }
91
+ end
92
+
93
+ def dav_match(*args)
94
+ get *args
95
+ dav_propfind *args
96
+ end
97
+
98
+ def webdav_resource(*resources, &block)
99
+ options = resources.extract_options!
100
+
101
+ if apply_common_behavior_for(:webdav_resource, resources, options, &block)
102
+ return self
103
+ end
104
+
105
+ sub_block = lambda do
106
+ yield if block_given?
107
+
108
+ if parent_resource.actions.include?(:create)
109
+ collection do
110
+ post :create
111
+ end
112
+ end
113
+
114
+ if parent_resource.actions.include?(:new)
115
+ new do
116
+ get :new
117
+ end
118
+ end
119
+
120
+ member do
121
+ if parent_resource.actions.include?(:show)
122
+ dav_match :show
123
+ end
124
+ get :edit if parent_resource.actions.include?(:edit)
125
+ put :update if parent_resource.actions.include?(:update)
126
+ delete :destroy if parent_resource.actions.include?(:destroy)
127
+ end
128
+ end
129
+
130
+ if Rails.version <= '3.1'
131
+ resource_scope(WebDAVSingletonResource.new(resources.pop, options), &sub_block)
132
+ else
133
+ resource_scope(:webdav_resource, WebDAVSingletonResource.new(resources.pop, options), &sub_block)
134
+ end
135
+
136
+ self
137
+ end
138
+
139
+ def webdav_resources(*resources, &block)
140
+ options = resources.extract_options!
141
+
142
+ if apply_common_behavior_for(:webdav_resources, resources, options, &block)
143
+ return self
144
+ end
145
+
146
+ sub_block = lambda do
147
+ yield if block_given?
148
+
149
+ opts = []
150
+ collection do
151
+ if parent_resource.actions.include?(:index)
152
+ dav_match :index
153
+ opts << [:get, :propfind]
154
+ end
155
+
156
+ if parent_resource.actions.include?(:create)
157
+ post :create
158
+ opts << :post
159
+ end
160
+
161
+ if parent_resource.actions.include?(:update_all)
162
+ put :index, :action => :update_all
163
+ opts << :put
164
+ end
165
+ dav_options :index, :to => dav_options_response(opts)
166
+ end
167
+
168
+ if parent_resource.actions.include?(:new)
169
+ new do
170
+ dav_match :new
171
+ put :new, :action => :create
172
+ dav_options :new, :to => dav_options_response(:get, :put, :propfind, :options)
173
+ end
174
+ end
175
+
176
+ member do
177
+ opts = []
178
+ if parent_resource.actions.include?(:show)
179
+ dav_match :show
180
+ opts << :get
181
+ opts << :propfind
182
+ end
183
+
184
+ if parent_resource.actions.include?(:update)
185
+ put :update
186
+ opts << :put
187
+ end
188
+
189
+ if parent_resource.actions.include?(:destroy)
190
+ delete :destroy
191
+ opts << :delete
192
+ end
193
+
194
+ dav_options :show, :to => dav_options_response(opts)
195
+
196
+ if parent_resource.actions.include?(:edit)
197
+ dav_match :edit
198
+ dav_options :edit, :to => dav_options_response(:get, :propfind)
199
+ end
200
+ end
201
+ end
202
+
203
+ if Rails.version <= '3.1'
204
+ resource_scope(WebDAVResource.new(resources.pop, options), &sub_block)
205
+ else
206
+ resource_scope(:webdav_resources, WebDAVResource.new(resources.pop, options), &sub_block)
207
+ end
208
+
209
+ self
210
+ end
211
+
212
+ end
213
+ end
data/lib/railsdav.rb ADDED
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module Railsdav
4
+ autoload :ControllerExtensions, 'railsdav/controller_extensions'
5
+ autoload :RoutingExtensions, 'railsdav/routing_extensions'
6
+ autoload :RequestExtensions, 'railsdav/request_extensions'
7
+ autoload :Renderer, 'railsdav/renderer'
8
+
9
+ class MissingWebDAVMetadata < StandardError; end
10
+
11
+ WEBDAV_HTTP_VERBS = ActionDispatch::Request::RFC2518
12
+
13
+ Mime::Type.register_alias 'application/xml', :webdav
14
+
15
+ ActionController::Renderers.add :webdav do |response_type, options|
16
+ renderer = Railsdav::Renderer.new(self)
17
+ xml_str = renderer.respond_with response_type, options
18
+
19
+ Rails.logger.debug "WebDAV response:\n#{xml_str}"
20
+
21
+ response.headers['Depth'] = renderer.depth.to_s
22
+ response.headers['DAV'] = '1'
23
+
24
+ send_data xml_str,
25
+ :content_type => Mime::XML,
26
+ :status => :multi_status
27
+ end
28
+
29
+ ActionController::Base.send :include, Railsdav::ControllerExtensions
30
+ ActionDispatch::Request.send :include, Railsdav::RequestExtensions
31
+
32
+ end # module Railsdav
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: railsdav
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Willem van Kerkhof
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-18 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Provides basic Rails 3/Rails 4 extensions for making your business resources
15
+ accessible via WebDAV. This gem does by no means by no means implement the full
16
+ WebDAV semantics, but it suffices to access your app with client(-libs) such as
17
+ Konqueror, cadaver, davfs2 or NetDrive
18
+ email: wvk@consolving.de
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - README.md
24
+ - init.rb
25
+ - lib/railsdav.rb
26
+ - lib/railsdav/routing_extensions.rb
27
+ - lib/railsdav/request_extensions.rb
28
+ - lib/railsdav/renderer/response_collector.rb
29
+ - lib/railsdav/renderer/response_type_selector.rb
30
+ - lib/railsdav/controller_extensions.rb
31
+ - lib/railsdav/renderer.rb
32
+ homepage: http://github.com/wvk/railsdav
33
+ licenses: []
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 1.8.23
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: Make your Rails 3/4 resources accessible via WebDAV
56
+ test_files: []
57
+ has_rdoc: