redmine_webdav/ 000755 000000 000000 00000000000 11412602363 014126 5 ustar 00root wheel 000000 000000 redmine_webdav/LICENSE 000644 000000 000000 00000002040 11410651736 015135 0 ustar 00root wheel 000000 000000 Copyright (c) 2006 Stuart Eccles
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. redmine_webdav/README.textile 000644 000000 000000 00000005165 11412654740 016500 0 ustar 00root wheel 000000 000000 h1. Introduction
This WebDav plugin is mainly a POC (proof of concept) for accessing files and documents using WebDav protocol.
So you want a rails application to give you a webdav? Good, railsdav can do this for you. However, it might take you an hour or two as well to figure out how it works :)
This plugin is a copy of an existing railsdav plugin (railsdav) with some modifications to make it run with REDMINE.
This was done using REDMINE r3394 (=> 0.9.1 +)
h1. Setup
h3. 1. Put this plugin into vendor/plugins
Plugin must be installed in vendor/plugins/redmine_webdav
h3. 2. Get the required gems
Railsdav needs these 2 plugins:
* unicode
* shared-mime-info
Under linux, install is done with:
* sudo gem install unicode
* sudo gem install shared-mime-info
h3. 3. Configure your web server
TODO
h3. 4. Restart your web server
h3. 5. Configure REDMINE
A new permission is now available (Webdav access) and you have to assign it to the roles you need
h3. 6. Set up your project
Don't forget to activate the webdav module in the projects where you want to access files over WevDav
h3. 7. Use webdav to manage files and documents
You have to use a WebDav client in order to use the plugin.
For MacOSX (webdav access is natively supported):
* Open the finder and choose "Go to -> Connect to server..."
* enter "http://myserver/redmine/webdav" as the url of the server
* enter your credentials (login/password)
* You're in
For linux:
TODO
For Windows:
WebDrive seems to work (but is not a freeware)
DataFreeway is strange...
h1. Original author's README:
Railsdav (WebDAV for Ruby On Rails)
===================================
THIS IS A MODIFIED VERSION OF THE ORIGINAL Railsdav, created by Marcello Nuccio. I have done it because I was not able to make to use the original one.
READ COMMENTS IN lib/acts_as_webdav.rb TO START!
Railsdav is a plugin which allows for custom WebDAV controllers to be added to a Ruby On Rails application to expose functionality as filesystems to WebDAV clients such as Mac OS X Finder, Windows Network Places, Dreamweaver etc.
Railsdav requires the following gems in addition to rails
* unicode (gem install unicode)
* shared-mime-info (gem install shared-mime-info)
Railsdav was created by Stuart Eccles (http://www.liverail.net/). I'd like to thank the following people for assisting with Railsdav
* Albert Ramstedt who contributed bug fixes for a variety of code issues and helped with re-structuring the plugin
* Alex MacCaw
* Fabien Franzen redmine_webdav/app/ 000755 000000 000000 00000000000 11410651736 014714 5 ustar 00root wheel 000000 000000 redmine_webdav/config/ 000755 000000 000000 00000000000 11412602014 015364 5 ustar 00root wheel 000000 000000 redmine_webdav/init.rb 000644 000000 000000 00000001635 11412631520 015420 0 ustar 00root wheel 000000 000000 #WebDav plugin for REDMINE
require 'redmine'
require File.dirname(__FILE__) + '/lib/railsdav'
ActionController::Base.send(:extend, Railsdav::Acts::Webdav::ActMethods)
#WebDAV methods
WEBDAV_HTTP_METHODS = %w(propfind mkcol move copy lock unlock options delete put proppatch) #you can add other methods here
WEBDAV_HTTP_METHODS.each do |method|
ActionController::Routing::HTTP_METHODS << method.to_sym
end
WEBDAV_HTTP_METHOD_LOOKUP = WEBDAV_HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
ActionController::Request::HTTP_METHODS.concat(WEBDAV_HTTP_METHODS)
ActionController::Request::HTTP_METHOD_LOOKUP.merge!(WEBDAV_HTTP_METHOD_LOOKUP)
Redmine::Plugin.register :redmine_webdav do
name 'WebDav plugin'
author 'Arnaud Martel'
description 'WebDav plugin for managing files inside projects'
version '0.0.1'
project_module :webdav do
permission :webdav_access, :webdav => :webdav
end
end redmine_webdav/lib/ 000755 000000 000000 00000000000 11412576354 014706 5 ustar 00root wheel 000000 000000 redmine_webdav/templates/ 000755 000000 000000 00000000000 11410651736 016132 5 ustar 00root wheel 000000 000000 redmine_webdav/templates/propfind.xml.builder 000644 000000 000000 00000000736 11411134445 022122 0 ustar 00root wheel 000000 000000 xml.instruct!
xml.D(:multistatus, "xmlns:D" => "DAV:") do
resources.each do |resource|
xml.D :response do
xml.D :href, resource.href
xml.D :propstat do
xml.D :prop do
resource.properties.each do |property, value|
xml.D(property, value)
end
xml.D :resourcetype do
xml.D :collection if resource.collection?
end
end
xml.D :status, resource.status
end
end
end
end
redmine_webdav/templates/proppatch.xml.builder 000644 000000 000000 00000002221 11410651736 022276 0 ustar 00root wheel 000000 000000 xml.D(:multistatus, "xmlns:D" => "DAV:") do
xml.D :response do
xml.D :href, URI.escape(resource.href)
remove_properties.each do |remove_property|
xml.D :propstat do
xml.D :prop do
xml.tag! remove_property.name.to_sym, remove_property.attributes
end
sym = "remove_#{remove_property.name}".to_sym
if resource.respond_to?(sym)
xml.D(:status, resource.__send__(sym))
else
xml.D :status, "HTTP/1.1 200 OK"
end
end
end
set_properties.each do |set_property|
xml.D :propstat do
xml.D :prop do
xml.D set_property.name.to_sym, set_property.attributes
end
sym = "set_#{set_property.name}".to_sym
if resource.respond_to?(sym)
method = resource.method(sym)
if method.arity == 1 && set_property.children && !set_property.children.empty?
xml.D :status, method.call(set_property.children.first.to_s)
else
xml.D :status, method.call
end
else
xml.D :status, "HTTP/1.1 200 OK"
end
end
end
xml.D :responsedescription
end
end
redmine_webdav/lib/acts_as_webdav.rb 000644 000000 000000 00000034334 11412636405 020201 0 ustar 00root wheel 000000 000000 # Copyright (c) 2006 Stuart Eccles
# Released under the MIT License. See the LICENSE file for more details.
#
# Modified by Marcello Nuccio.
#
# Here is an example configuration:
#
# A model:
#
# class WebdavResource < Railsdav::FileResource
# end
#
# A controller:
#
# class WebDavController < ApplicationController
# skip_before_filter :verify_authenticity_token
# acts_as_webdav
# end
#
# the controller must then have a route like:
#
# ActionController::Routing::Routes.draw do |map|
# map.connect 'home/*path_info', :controller => 'web_dav', :action => 'webdav'
# map.root :controller => 'web_dav', :action => 'webdav'
# end
#
# and you should add webdav methods into config/environments.rb:
#
# ActionController::ACCEPTED_HTTP_METHODS.merge(%w{propfind mkcol move copy})
#
#
# Add authentication using rails authenticate_with_http_basic method.
#
require 'action_controller'
require 'unicode'
module Railsdav
module Acts #:nodoc:
module Webdav #:nodoc:
ACTIONS = %w(lock unlock options propfind proppatch mkcol delete put copy move)
VERSIONS = %w(1 2)
module ActMethods
def acts_as_webdav(options = {})
class_inheritable_accessor :dav_actions
class_inheritable_accessor :dav_versions
class_inheritable_accessor :resource_model
self.resource_model = options[:resource_model]
options[:extra_actions] ||= []
options[:extra_dav_versions] ||= []
self.dav_actions = ACTIONS + options[:extra_actions]
self.dav_versions = VERSIONS + options[:extra_dav_versions]
include InstanceMethods unless included_modules.include?(InstanceMethods)
#include Railsdav::Webdav::Callbacks
#hide_action(*(ACTIONS.map {|method| "webdav_#{method}" }))
end
end
module InstanceMethods
def webdav
method = "webdav_#{request.head? ? "head" : request.method}"
raise UnknownWebDavMethodError unless respond_to?(method, true)
begin
set_depth
set_path_info
# if @path_info.split("/").last[0,1] != "."
check_read(@path_info)
__send__(method)
# else
# render :nothing => true, :status => 404
# end
rescue BaseError => error
render :nothing => true, :status => error.http_status
end
end
def rootwebdav
raise UnknownWebDavMethodError unless (request.method == :propfind || request.method == :options)
if request.method == :options
webdav_options
else
begin
resources = Project.find(:all, :conditions => Project.visible_by(User.current))
href = url_for(:only_path => true, :path_info => params[:path_info])
first = resources.first
$KCODE = 'UTF8'
#RAILS_DEFAULT_LOGGER.info "Dans propfind"
xml = " "
xml << ""
xml << "#{href}"
xml << ""
xml << "webdav"
xml << "#{first.created_on.xmlschema}"
xml << "#{first.updated_on.httpdate}"
xml << "1-0-#{first.created_on.to_i}"
xml << "httpd/unix-directory"
xml << "0"
#:displayname, :creationdate, :getlastmodified,:getetag, :getcontenttype, :getcontentlength
xml << ""
xml << ""
xml << ""
xml << ""
xml << "HTTP/1.1 200 OK"
xml << ""
xml << ""
resources.each do |project|
if @user.allowed_to?(:webdav_access, project)
xml << ""
xml << "" << File.join(href, project.identifier) << ""
xml << ""
xml << "#{project.identifier}"
xml << "#{project.created_on.xmlschema}"
xml << "#{project.updated_on.httpdate}"
xml << "1-0-#{project.created_on.to_i}"
xml << "httpd/unix-directory"
xml << "0"
xml << ""
xml << ""
xml << ""
xml << ""
xml << "HTTP/1.1 200 OK"
xml << ""
xml << ""
end
end
xml << ""
render :text => xml, :status => 207, :layout => false, :content_type => "application/xml"
rescue BaseError => error
render :nothing => true, :status => error.http_status
end
end
end
def webdavnf
render :nothing => true, :status => 404
end
private
def webdav_options
response.headers['DAV'] = dav_versions.join(",")
response.headers['MS-Author-Via'] = "DAV"
response.headers["Allow"] = dav_actions.map(&:upcase).join(",")
render :nothing => true, :status => 200
end
def webdav_lock
#TODO implementation for now return a 200 OK
resource = find_resource_by_path(@path_info)
raise NotFoundError unless resource
render :nothing => true, :status => 200
end
def webdav_unlock
#TODO implementation for now return a 200 OK
resource = find_resource_by_path(@path_info)
raise NotFoundError unless resource
render :nothing => true, :status => 200
end
PROPFIND_TEMPLATE = File.dirname(__FILE__) + '/../templates/propfind.xml.builder'
def webdav_propfind
resource = find_resource_by_path(@path_info)
raise NotFoundError unless resource
resources = get_dav_resource_props(resource)
$KCODE = 'UTF8'
#RAILS_DEFAULT_LOGGER.info "Dans propfind"
xml = " "
resources.flatten.each do |resource|
xml << ""
xml << "" << resource.href << ""
xml << ""
resource.properties.each do |property, value|
if !value
value = ""
end
xml << "#{value}"
end
xml << ""
xml << "" if resource.collection?
xml << ""
xml << ""
xml << "#{resource.status}"
xml << ""
xml << ""
end
xml << ""
render :text => xml, :status => 207, :layout => false, :content_type => "application/xml"
#render(:file => PROPFIND_TEMPLATE, :status => 207, :locals => { :resources => resources.flatten } )
end
PROPPATCH_TEMPLATE = File.dirname(__FILE__) + '/../templates/proppatch.xml.builder'
def webdav_proppatch
resource = find_resource_by_path(@path_info)
raise NotFoundError unless resource
begin
req_xml = REXML::Document.new request.raw_post
rescue REXML::ParseException
raise BadRequestBodyError
end
ns = { "" => "DAV:" }
remove_properties = REXML::XPath.match(req_xml, "/propertyupdate/remove/prop/*", ns)
set_properties = REXML::XPath.match(req_xml, "/propertyupdate/set/prop/*", ns)
render(:file => PROPPATCH_TEMPLATE, :status => 207,
:locals => { :resource => resource,
:remove_properties => remove_properties,
:set_properties => set_properties })
end
def webdav_mkcol
check_write(@path_info)
mkcol_for_path(@path_info)
render :nothing => true, :status => 201
end
def webdav_delete
check_write(@path_info)
resource = find_resource_by_path(@path_info)
if resource
resource.delete!
end
render :nothing => true, :status => 204
end
def webdav_put
check_write(@path_info)
write_content_to_path(@path_info, request.raw_post)
render :nothing => true, :status => 201
end
def with_source_and_destination_resources
begin
uri = URI.parse(request.env['HTTP_DESTINATION'].chomp('/'))
base_path = url_for(:only_path => true, :path_info => "")
raise ForbiddenError if uri.path !~ /^#{Regexp.escape(base_path)}\//
logger.debug "destination_path = #{$'.inspect}"
dest_path = $'
rescue URI::InvalidURIError
raise BadGatewayError
end
source_resource = find_resource_by_path(@path_info)
raise NotFoundError unless source_resource
dest_resource = find_resource_by_path(dest_path)
raise PreconditionFailsError if dest_resource && !overwrite_destination?
yield(source_resource, dest_path)
render :nothing => true, :status => (dest_resource ? 204 : 201)
#render :nothing => true, :status => 201
end
def webdav_copy
with_source_and_destination_resources do |source_resource, dest_path|
check_write(dest_path)
copy_to_path(source_resource, CGI.unescape(dest_path), @depth)
end
end
def webdav_move
with_source_and_destination_resources do |source_resource, dest_path|
check_write(dest_path)
move_to_path(source_resource, CGI.unescape(dest_path), @depth)
end
end
def webdav_get
resource = find_resource_by_path(@path_info)
raise NotFoundError unless resource
data_to_send = resource.data
raise NotFoundError if data_to_send.blank?
response.headers["Last-Modified"] = resource.getlastmodified
if data_to_send.kind_of?(File)
send_file File.expand_path(data_to_send.path), :filename => resource.displayname, :stream => true
else
send_data data_to_send, :filename => resource.displayname
end
end
def webdav_head
resource = find_resource_by_path(@path_info)
raise NotFoundError if resource.blank?
response.headers["Last-Modified"] = resource.getlastmodified
render :nothing => true, :status => 200
end
private
#
# These are default implementations
# If you do not want to implement one of them, dont put
# the corresponding HTTP method in ACCEPTED_HTTP_METHODS
#
def mkcol_for_path(path)
raise ForbiddenError unless resource_model
resource_model.mkcol_for_path(@project, path)
end
def move_to_path(resource, dest_path, depth)
raise ForbiddenError unless resource_model
resource.move_to_path(dest_path, depth)
end
def write_content_to_path(path, content)
raise ForbiddenError unless resource_model
resource_model.write_content_to_path(@project, path, content)
end
def copy_to_path(resource, dest_path, depth)
raise ForbiddenError unless resource_model
resource_model.copy_to_path(path, content)
end
def find_resource_by_path(path)
raise ForbiddenError unless resource_model
href = url_for(:only_path => true, :path_info => params[:path_info])
resource_model.initialize_by_path_and_href(@project, path, href)
end
def overwrite_destination?
request.env['HTTP_OVERWRITE'] == 'T'
end
def get_dav_resource_props(resource)
@depth -= 1
ret_set = [ resource ]
ret_set += (resource.children.map do |child|
get_dav_resource_props(child)
end) if @depth >= 0 && resource.children
ret_set
end
def set_path_info
raise ForbiddenError if params[:path_info].nil?
logger.debug "params[:path_info] = #{params[:path_info].inspect}"
path = params[:path_info].join('/')
@path_info = case request.env["HTTP_USER_AGENT"]
when /Microsoft|Windows/
logger.info("CONVERTED: " + Iconv.iconv('UTF-8', 'latin1', URI.unescape(path)).first)
Iconv.iconv('UTF-8', 'latin1', URI.unescape(path)).first
when /cadaver/
URI.unescape(URI.unescape(path))
else
URI.unescape(path)
end
end
def set_depth
depth_header = request.env['HTTP_DEPTH']
@depth = depth_header == 'infinity' ? 50 : (Integer(depth_header || 1) rescue 1)
end
def check_read(path)
@pinfo = path.split("/")
if @pinfo.length > 0
case @pinfo[0]
when "files"
raise ForbiddenError unless @user.allowed_to?(:view_files, @project)
when "documents"
raise ForbiddenError unless @user.allowed_to?(:view_documents, @project)
end
end
end
def check_write(path)
@pinfo = path.split("/")
if @pinfo.length > 0
case @pinfo[0]
when "files"
raise ForbiddenError unless @user.allowed_to?(:manage_files, @project)
when "documents"
raise ForbiddenError unless @user.allowed_to?(:manage_documents, @project)
end
else
raise ForbiddenError
end
end
end
end
end
end
redmine_webdav/lib/attachment_patch.rb 000644 000000 000000 00000003316 11412576746 020552 0 ustar 00root wheel 000000 000000 require_dependency 'attachment'
# Patches Redmine's Issues dynamically. Adds a relationship
# Issue +belongs_to+ to Deliverable
module WebDavAttachmentPatch
def self.included(base) # :nodoc:
base.extend(WebDavClassMethods)
base.send(:include, WebDavInstanceMethods)
# Same as typing in the class
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
class << self
# I dislike alias method chain, it's not the most readable backtraces
end
end
end
module WebDavClassMethods
end
module WebDavInstanceMethods
# Wraps the association to get the Deliverable subject. Needed for the
# Query and filtering
def webdavfile=(incoming_file)
unless incoming_file.nil?
@temp_file = incoming_file
if @temp_file.size >= 0
self.filename = sanitize_webdavfilename(@temp_file.original_filename)
self.disk_filename = Attachment.disk_filename(filename)
self.content_type = @temp_file.content_type.to_s.chomp
if content_type.blank?
self.content_type = Redmine::MimeType.of(filename)
end
self.filesize = @temp_file.size
end
end
end
private
def sanitize_webdavfilename(value)
# get only the filename, not the whole path
just_filename = value.gsub(/^.*(\\|\/)/, '')
# NOTE: File.basename doesn't work right with Windows paths on Unix
# INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/'))
# Finally, replace all non alphanumeric, hyphens or periods with underscore
@filename = just_filename
end
end
end
# Add module to Issue
Attachment.send(:include, WebDavAttachmentPatch)
redmine_webdav/lib/file_resource.rb 000644 000000 000000 00000021620 11412636316 020055 0 ustar 00root wheel 000000 000000 # Copyright (c) 2006 Stuart Eccles
# Released under the MIT License. See the LICENSE file for more details.
# The base_dir parameter can be a string for a directory or a symbol for a method which is run for every
# request allowing the base directory to be changed based on the request
#
# If the parameter :absolute = true the :base_dir setting will be treated as an absolute path, otherwise
# the it will be taken as a directory underneath the RAILS_ROOT
#
require 'shared-mime-info'
module Railsdav
class FileResource
include Resource
@@logger = Logger.new(STDOUT)
WEBDAV_PROPERTIES = [ :displayname, :creationdate, :getlastmodified,
:getetag, :getcontenttype, :getcontentlength ]
class_inheritable_accessor :file_options
self.file_options = {
:base_url => '',
:max_propfind_depth => 1
}
def initialize(*args)
@href=""
@project = args.first
#RAILS_DEFAULT_LOGGER.info "Dans fileresource.initialize projectname= #{@project.name}"
#arg[1] : / [/fichier]
pinfo=args[1].split("/")
@level = pinfo.length
@isdir = true
if @level == 0
@container = @project
end
if @level == 1
if pinfo[0] == "files"
@container = "files"
end
if pinfo[0] == "documents"
@container = "documents"
end
end
if @level > 1 && pinfo[0] == "files"
@container = @project.versions.find_by_name(pinfo[1])
end
if @level > 1 && pinfo[0] == "documents"
@container = @project.documents.find_by_title(pinfo[1])
end
if !@container && pinfo[0] == "files" && @level==2
@file = @project.attachments.find(:first, :conditions => [ "filename = ?", pinfo[1] ])
@container = @project
@isdir = false
end
if @level > 2
@isdir = false
if @container
@file = @container.attachments.find(:first, :conditions => [ "filename = ?", pinfo[2] ])
end
end
if !@isdir && @file
@stat = File.lstat(@file.diskfile)
end
if args.last.is_a?(String)
@href = args.last
@href = File.join(@href, '') if collection?
end
end
def self.initialize_by_path_and_href(project, path, href)
do_file_action do
r = new(project, path, href)
r if r.valid?
end
end
def collection?
@isdir
end
def valid?
(@isdir && @container) || @file
end
def delete!
self.class.do_file_action do
if @file
@container.attachments.delete(@file)
else
if @container.is_a?(Document)
@container.destroy
end
end
end
end
def children
resources = []
case @level
when 0
resources << self.class.new(@project, "files", File.join(@href, "files")) if User.current.allowed_to?(:view_files, @project)
resources << self.class.new(@project, "documents", File.join(@href, "documents")) if User.current.allowed_to?(:view_document, @project)
when 1
if @container == "files"
@project.versions.each do |version|
resources << self.class.new(@project, File.join("files", version.name), File.join(@href, version.name))
end
@project.attachments.each do |attach|
resources << self.class.new(@project, File.join("files", attach.filename), File.join(@href, attach.filename))
end
else
@project.documents.each do |document|
resources << self.class.new(@project, File.join("documents", document.title), File.join(@href, document.title))
end
end
when 2
if @isdir
if @container.is_a?(Document)
foldername = @container.title
root="documents"
else
foldername = @container.name
root="files"
end
@container.attachments.each do |attach|
resources << self.class.new(@project, File.join(root,foldername,attach.filename), File.join(@href, attach.filename))
end
end
end
resources
end
def properties
props = {}
WEBDAV_PROPERTIES.each do |method|
props[method] = send(method) if respond_to?(method)
end
props
end
def displayname
case @level
when 0
@project.name
when 1
@container
else
if @isdir
if @container.is_a?(Document)
@container.title
else
@container.name
end
else
@file.filename
end
end
end
def creationdate
cdate = @project.created_on
if @level > 1
if @isdir
cdate = @container.created_on
else
cdate = @stat.ctime
end
end
cdate.xmlschema
end
def getlastmodified
cdate = @project.updated_on
if @level > 1
if @isdir
if @container.is_a?(Version)
cdate = @container.updated_on
end
else
cdate = @stat.mtime
end
end
cdate.httpdate
end
def getetag
case @level
when 0
sprintf('%x-%x-%x', @project.id, 0, @project.updated_on.to_i)
when 1
sprintf('%x-%x-%x', (@project.id * 10) + @container.length, 0, @project.updated_on.to_i)
else
if @isdir
if @container.is_a?(Version)
sprintf('%x-%x-%x', @container.id, 0, @container.updated_on.to_i)
else
sprintf('%x-%x-%x', @container.id, 0, @container.created_on.to_i)
end
else
sprintf('%x-%x-%x', @file.id, @file.filesize, @file.created_on.to_i)
end
end
end
def getcontenttype
if collection?
"httpd/unix-directory"
else
mimetype = MIME.check_globs(displayname).to_s
mimetype.blank? ? "application/octet-stream" : mimetype
end
end
def getcontentlength
collection? ? nil : @stat.size
end
def data
if ! @isdir
File.new(@file.diskfile)
end
end
def filecontent
if ! @isdir
File.open(@file.diskfile, 'rb') { |f| f.read }
end
end
def self.mkcol_for_path(project, path)
#Create directory
pinfo = path.split("/")
raise ForbiddenError unless pinfo.length == 2
raise ForbiddenError unless pinfo[0] == "documents"
container = project.documents.find_by_title(pinfo[1])
if !container
@doc = project.documents.build({ :title => pinfo[1],
:description => 'Created from WEBDAV',
:category_id => DocumentCategory.first.id})
@doc.save
end
end
def self.write_content_to_path(project, path, content)
#Create a file
pinfo = path.split("/")
case pinfo.length
when 2
if pinfo[0] == "files"
container = project
file = project.attachments.find(:first, :conditions => [ "filename = ?", pinfo[1] ])
end
when 3
if pinfo[0] == "files"
container = project.versions.find_by_name(pinfo[1])
file = container.attachments.find(:first, :conditions => [ "filename = ?", pinfo[2] ])
end
if pinfo[0] == "documents"
container = project.documents.find_by_title(pinfo[1])
file = container.attachments.find(:first, :conditions => [ "filename = ?", pinfo[2] ])
end
end
if container
if file
container.attachments.delete(file)
end
uploaded_file = ActionController::UploadedTempfile.new(pinfo.last)
uploaded_file.binmode
uploaded_file.write(content)
# uploaded_file.flush
uploaded_file.original_path = pinfo.last
uploaded_file.rewind
a = Attachment.create(:container => container,
:webdavfile => uploaded_file,
:description => "",
:author => User.current)
if a.new_record?
a.save
end
uploaded_file.close!
end
end
def move_to_path(dest_path, depth)
pinfo = dest_path.split("/")
raise ForbiddenError unless !@isdir || (@container.is_a?(Document) && pinfo.length == 2)
self.class.do_file_action do
if !@isdir
FileResource.write_content_to_path(@project, dest_path, filecontent)
delete!
else
@container.title = CGI.unescape(pinfo[1])
@container.save
end
end
end
def self.copy_to_path(dest_path, depth)
raise ForbiddenError unless !@isdir
self.class.do_file_action do
self.write_content_to_path(@project, dest_path, filecontent)
end
end
def self.do_file_action
begin
yield
rescue Errno::ENOENT, Errno::EEXIST
raise ConflictError
rescue Errno::EPERM, Errno::EACCES
raise ForbiddenError
rescue Errno::ENOSPC
raise InsufficientStorageError
end
end
end
end
redmine_webdav/lib/railsdav.rb 000644 000000 000000 00000000365 11412576342 017041 0 ustar 00root wheel 000000 000000 # = About lib/railsdav.rb
$:.unshift File.expand_path(File.dirname(__FILE__))
module Railsdav
VERSION = '0.1.1'
end
require 'webdav_errors'
require 'webdav_resource'
require 'acts_as_webdav'
require 'file_resource'
require 'attachment_patch' redmine_webdav/lib/webdav_errors.rb 000644 000000 000000 00000002235 11410651736 020075 0 ustar 00root wheel 000000 000000 require 'active_support'
module Railsdav
class BaseError < StandardError
end
class LockedError < BaseError
@@http_status = 423
cattr_reader :http_status
end
class InsufficientStorageError < BaseError
@@http_status = 507
cattr_reader :http_status
end
class ConflictError < BaseError
@@http_status = 405
cattr_reader :http_status
end
class ForbiddenError < BaseError
@@http_status = 403
cattr_reader :http_status
end
class BadGatewayError < BaseError
@@http_status = 502
cattr_reader :http_status
end
class PreconditionFailsError < BaseError
@@http_status = 412
cattr_reader :http_status
end
class NotFoundError < BaseError
@@http_status = 404
cattr_reader :http_status
end
class UnSupportedTypeError < BaseError
@@http_status = 415
cattr_reader :http_status
end
class UnknownWebDavMethodError < BaseError
@@http_status = 405
cattr_reader :http_status
end
class BadRequestBodyError < BaseError
@@http_status = 400
cattr_reader :http_status
end
class TODO409Error < BaseError
@@http_status = 409
cattr_reader :http_status
end
end
redmine_webdav/lib/webdav_resource.rb 000644 000000 000000 00000001272 11410651736 020410 0 ustar 00root wheel 000000 000000 # Copyright (c) 2006 Stuart Eccles
# Released under the MIT License. See the LICENSE file for more details.
module Railsdav
module Resource
def status
gen_status(200, "OK")
end
def displayname
URI.escape(@displayname).gsub(/\+/, '%20') if @displayname
end
def href
@href.gsub(/\+/, '%20') if @href
end
def gen_element(element_name, text = nil, attributes = {})
element = REXML::Element.new(element_name)
element.text = text if text
attributes.each {|k, v| element.attributes[k] = v }
element
end
def gen_status(status_code, reason_phrase)
"HTTP/1.1 #{status_code} #{reason_phrase}"
end
end
end
redmine_webdav/config/routes.rb 000644 000000 000000 00000000450 11412620111 017227 0 ustar 00root wheel 000000 000000 ActionController::Routing::Routes.draw do |map|
map.connect 'webdav', :controller => 'webdav', :action => 'rootwebdav'
map.connect 'webdav/:id/*path_info', :controller => 'webdav', :action => 'webdav'
map.connect 'webdav/*path_info', :controller => 'webdav', :action => 'webdavnf'
end
redmine_webdav/app/controllers/ 000755 000000 000000 00000000000 11410651736 017262 5 ustar 00root wheel 000000 000000 redmine_webdav/app/models/ 000755 000000 000000 00000000000 11410651736 016177 5 ustar 00root wheel 000000 000000 redmine_webdav/app/models/webdav.rb 000644 000000 000000 00000000052 11410651736 017771 0 ustar 00root wheel 000000 000000 class Webdav < Railsdav::FileResource
end
redmine_webdav/app/controllers/webdav_controller.rb 000644 000000 000000 00000001516 11412630656 023325 0 ustar 00root wheel 000000 000000 class WebdavController < ApplicationController
acts_as_webdav :resource_model => Webdav
before_filter :find_project, :authorize, :find_user
private
def find_project
# @project variable must be set before calling the authorize filter
if params[:id]
@project = Project.find(params[:id])
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_user
User.current = find_current_user
@user = User.current
end
# Authorize the user for the requested action
def authorize(ctrl = params[:controller], action = params[:action], global = false)
case action
when "rootwebdav", "webdavnf"
allowed = true
else
allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global)
end
allowed ? true : deny_access
end
end