I recently made a proof-of-concept to get MS Word to edit documents in a Rails app and save them back to the server, using Devise for authentication. It wasn't straightforward, so I thought I'd mention the steps I took to get it done.
I used Rails 3.2.12 with
rack_dav providing the WebDAV functionality.
There's also
dav4rack which was based on rack_dav, and adds a few features.
I started with rack_dav, and had all of the kinks worked out except for the authentication, when I noticed dav4rack included authentication. So I switched to dav4rack, but I had some trouble with it, so I switched back to rack_dav because I feared I'd get into another day-or-two-long yak shaving session.
You might check it out though, and see if it works just as well.
Anyway, here's the basis of what I did.
1.
Add the gem to my Gemfile
(and run
bundle install
):
gem 'rack_dav'
2.
Mount it in my routes.rb
, wrapped with basic authentication:
webdav = RackDAV::Handler.new(:root => 'private/docs/', :resource_class => LockableFileResource)
webdav_with_auth = Rack::Auth::Basic.new(webdav) do |username, password|
user = User.find_for_authentication(:email => username)
user && user.valid_password?(password)
end
mount webdav_with_auth, at: "/webdav_docs"
3.
Create my lockable file resource, which doesn't really do any locking (since this was just a PoC), so you probably want to fix that:
class AuthenticableFileResource < RackDAV::FileResource
def lock(locktoken, timeout, lockscope=nil, locktype=nil, owner=nil)
puts "token: #{locktoken}"
puts "timeout: #{timeout}"
puts "scope: #{lockscope}"
puts "type: #{locktype}"
puts "owner: #{owner}"
@@owner ||= owner
@@locktype ||= 'exclusive'
@@lockscope ||= 'all day'
return [60, @@lockscope, @@locktype, @@owner]
end
def unlock(a)
@@owner = nil
@@locktype = nil
@@lockscope = nil
return true
end
end
4.
Tell the browser to instruct Word to open the file: For users on the web, we don't want the default rendering of the WebDAV root, because we'd like these docs to be editable in Word, which means you have to tell Word to open it. While you could add this to your own resource collection, I just created an action to list the links to the files with the code to open it in Word. Here's the basis of what you need in the view:
<script>
function officelink(link) {
try {
new ActiveXObject("SharePoint.OpenDocuments.4").EditDocument(link.href);
return false;
}
catch(e) {
try {
document.getElementById("winFirefoxPlugin").EditDocument(link.href);
return false;
}
catch(e2) {
return true;
}
}
}
</script>
<a href="/webdav_docs/inetfilter.doc" onclick="return officelink(this)">inetfilter.doc
<object id="winFirefoxPlugin" type="application/x-sharepoint" width="0" height="0" style="visibility: hidden;"></object>
Users who have Office 2010 installed on their system will also have those 2 plugins installed on their system, so we try to use them and return false (preventing the browser from following the link) if they have it. For those who don't, the JS provides a way to just download the file. See
OpenDocuments Control API and
FFWinPlugin Plug-in API for more info about what you can do with those plugins.
5.
Tell Rails not to parse params for our mounted rack_dav app. For whatever reason, Word will try to save the file back to the server,
PUT
ting it with the mime type application/xml, but it doesn't wrap in XML. I don't suppose it's supposed to, but it would be nice if it played well with Rails. Unfortunately, Rails understandably barfs when you tell it to parse parameters from XML and didn't send any XML. So, I had to create a patch and stuck it in an initializer. All we do is figure out if the request is for where we mounted rack_dav: if so, let rack_dav handle it without parsing the params. If not, just parse the params as normal:
module ActionDispatch
class ParamsParser
old_call = instance_method(:call)
define_method(:call) do |env|
if env["PATH_INFO"] =~ /\/webdav_docs\//
@app.call(env)
else
old_call.bind(self).(env)
end
end
end
end
There may be some security problems with this if rack_dav parses request parameters like Rails did before they put out their security updates in January and February 2013. I plan to review the rack_dav code to make sure it doesn't expose the same problems Rails had before I put this in production, and I'd recommend you do the same before you do.
6.
Tell Devise not to authenticate OPTIONS or PROPFIND requests. This is needed if you redirect your root url to a protected controller/action, because when Word makes requests using these methods, you'll get redirected and asked for authentication on a web page, which totally breaks the whole concept. This is probably in your
application_controller.rb
, unless you moved it. So if you moved it, you'll need to make the change there.
class ApplicationController < ActionController::Base
protect_from_forgery :unless=>:options_request?
before_filter :authenticate_user!, :unless=>:options_request?
def options_request?
request.method == "OPTIONS" || request.method == "PROPFIND"
end
end
That's all. Feel free to ask any questions if something is unclear or not working for you!
Hey! Why don't you make your life easier and subscribe to the full post
or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate
wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!
Leave a comment
Hi Sam,
I have a rails3 app. Got to implement the same functionality as you did. There are some uploaded word files. On click of "edit" link, the file need to be opened, give interface to edit and then save the file with proper formatting. Can you suggest any kind of gem or third-party service to do this. I have very less time to implement this functionality. Can you help me with short example.
Thanks in advance.
Posted by Rakesh Jha
on Jul 02, 2013 at 01:19 AM UTC - 6 hrs
Rakesh,
Unfortunately, I don't know of any gems to do this directly. The instructions above would work, but you'll need to put in some more effort to correctly lock the files, and audit the potential security issue I mentioned above.
On the other hand, it sounds like maybe you could edit them in the browser, so you might consider just storing them in HTML format using a typical wysiwyg editor, provide a download link for Word, and use something like
https://github.com/aquasync/ruby-ole to save as doc format (but this probably would only work well if you have a server that can run Windows, since it'll need word installed on it to do the automation, and even at that, you probably can't have more than a couple of people using it at the same time).
You might also have a look at Apache POI to see if it meets your needs:
http://poi.apache.org/hwpf/index.htmlFinally, Microsoft has explained how to build via XSLT, though I've not gone through it myself:
http://msdn.microsoft.com/en-us/library/office/ee8...(v=office.12).aspx
Sam
Posted by
Sammy Larbi
on Jul 02, 2013 at 06:22 AM UTC - 6 hrs
Hello Sam. Nice article! Would you be so kind to consult me?
I'm working on office documents editing using WebDAV on ASP.NET application. We use plugins, that you have talken about.
And IE10, FireFox and Chrome browsers works fine, but it seems that FireFox and Chrome used cookies from IE. If IE cookies are cleared, FireFox and Chrome cann't open office documents and OPTIONS/PROPFIND/... requests have no cookies on it headers.
Maybe you have a clue how to fix it? why requests through FFWinPlugin have no cookies on it?
A big thanks in advance.
Posted by Onyx
on Jul 12, 2013 at 11:25 AM UTC - 6 hrs
Sorry Onyx, I don't have any idea. Microsoft forums might be a better place to check. I've certainly seen some obscure questions answered there.
Posted by
Sammy Larbi
on Jul 13, 2013 at 06:11 PM UTC - 6 hrs
Leave a comment