WebDAV updates

I’ve made a lot of progress on SabreDAV, and felt like it was time for an update.

I pretty much implemented the entire webdav protocol at one point, but I was unhappy with the end-user API. Because this is a pet project and there’s no deadlines attached to it, I felt like I could take my sweet time to absolutely nail the structure.

So, I started over.. I learned a lot about the protocol, and by having a good overview on the entire protocol I felt like I did a much better job.. So right now, we have read-only support for OS/X and DavFS and an easy to use API.

If you’re a mac or linux user, give it a shot by connection to the endpoint : http://www.rooftopsolutions.nl/dav/ . If you open it directly from the browser it won’t give you a nice response. Also, I noticed that OS/X makes a lot of unnecessary requests when its just read only, so it will be a tad slow there. In order to fix this OS/X needs a DAV class 2 server, with locking support. It doesn’t have to be able to write, it just needs the locking HTTP methods.

SabreDAV in action!

What you see in the shot is a combination of 2 things, A normal file system directory containing the vcard and my resume, and a virtual ‘articles’ directory, which is based entirely on articles from the database.

SabreDAV currently has some helper classes that allow you to easily integrate with the file system, Sabre_DAV_FS_File and Sabre_DAV_FS_Directory. In order to mix virtual and real files I extended Sabre_DAV_FS_Directory and injected in an extra ‘fake’ directory.

Here’s the code to get this up and running, hope it makes sense:

<?php

// This is our root directory. Its a standard FS_Directory, but we're injecting the virtual 'articles' directory.
class RooftopDAV extends Sabre_DAV_FS_Directory {

    function getChildren() {

        $children = parent::getChildren();
        $children[] = new ArticlesDirectory();
        return $children;

    }

    function getChild($name) {

        if ($name=='articles') return new ArticlesDirectory();
        return parent::getChild($name);

    }

}

// This is our articles directory, its entirely virtual..  The only 2 methods we need to implement are getName and getChildren
class ArticlesDirectory extends Sabre_DAV_Directory {

    function getChildren() {

        // This is my database abstraction layer, you can make this work for any database (-library/-layer)
        $articles = Sabre_DAL_Manager::init()->getRecordSet('blog_posts');
        $children = array();

        foreach($articles->select() as $article) {

            $children[] = new Article($article);

        }
        return $children;

    }

    function getName() {

        return 'articles';

    }

}

// This is our article class.. We need to implement a couple of methods and we're ignoring all methods that change the file.
// Ideally we throw a 403 here, but for the proof of concept, we don't care
class Article implements Sabre_DAV_IFile {

    private $article;

    function __construct($article) {
        $this->article = $article;

    }

    function put($data) {

        // we'll just ignore this

    }

    function delete() {

        // ignored

    }

    function setName($name) {

        // ignored

    }

    function getName() {

        return $this->article['title'] . '.html';

    }

    function get() {

        return $this->article['description'];

    }

    function getSize() {

        return strlen($this->article['description']);

    }

    function getLastModified() {

        return strtotime($this->article['time']);

    }

}

// Setting up our root object
$root = new RooftopDAV('/home/evert/dev/rooftopsolutions.nl/davfolder');

// The tree is responsible for dispatching all http methods to our file and directory objects
$tree = new Sabre_DAV_ObjectTree($root);

// The server class does the hardcore protocol work
$server = new Sabre_DAV_Server($tree);

// We need to make sure the server knows on what url its located
$server->setBaseUri('/dav/');

// And off we go
$server->exec();

?>

You can check out the latest version of the code on the project page. Don’t depend your life on the current API, as it is subject to change until there’s a 1.0.