WebDAV API - Documentation
Vocabulary
- path
-
The path in our case is the REQUEST URI with first /webdav removed. So for /webdav/nodes/story/, path is /nodes/story/. For /webdav/nodes/story/1234/Content.html, path is /story/1234/Content.html.
- route
-
A route is a convenient way of naming a path split into pieces. So the previous path as array("root", "nodes", "story", "1234", "Content.html") as route.
- collection
-
A collection is the WebDAV term meaning "folder". A collection's URL always ends with /.
- resource
-
A resource is a collection or a file.
- member
-
A member is a collection item. Its a resource.
- route handler
-
This is an internal thing. Every module can give (see hook_webdav_definitions) an association with a path and a callback function. Two modules can give different members for the same path. Si a route handler is :
- array(
- 'route'=>array('aaa','%bbb',...), // route of the handler
- 'module'=>'source_module_1', // path owner
- 'definitions' => array(
- array(
- 'module'=>source_module_1_name,
- 'members callback'=>...
- 'arguments'=>....
- ),
- array(
- 'module'=>source_module_2_name,
- 'members callback'=>...
- 'arguments'=>....
- ),
- ...
- )
- )
So route handlers can be seen as an aggregation of hook_webdav_definitions invocation result.
- resolved route handler
-
The same as route handler but with route replaced with the real route and with a new parameters member containing association key/values.
hook_webdav_definitions
Description
This hook enables modules to register webdav resource by their path.
- function hook_webdav_definitions() {
- $items=array(
- '/node/%node_type/%node'=>array(
- 'arguments'=>array(1,2),
- 'members callback'=>'webdav_node_get_node_members',
- 'put callback'=>'webdav_node_content_put',
- 'access' => array (WEBDAV_NODE_PERMS_ACCESS),
- ),
- );
- return $items;
- }
Path definition
A path can contain variable parts starting with %part_name. In this case, the system will search for a loader called $module_name.'_'.$part_name.'_load'. module_name is the name of the module which contains the hook. If loader is found, the loader result is used as argument value for part_name, else, the original matching path part is used as a string value.
if you use %%part_name, this will match the rest of the path from this point. As an example, when you have /my_collection/%%full_path matching /my_collection/aaa/bbb/ccc, you'll have full_path=>'aaa/bbb/ccc'. %% MUST BE UNIQUE AND AT THE END of the path. If the matching path don't have any "full path" (ex. /my_collection/), the result will be a / value.
As MENU API, every path should have a definition entry to be recognized. This is the case for collections but also for files. In most cases files are handled by a path like /aaa/bbb/ccc/%file_name.
WebDAV operation
Many operation can be done on a resource : populate members, get, put, delete, move, copy, etc... the definition block will contains callback function for each single operation that can be done on the define resource. So, there is no "hooks" for deleting, copying, getting, putting, etc. a webdav resource. Everything is done by callback as it is with Menu API. So, hook_webdav_definitions is actually the only real hook in this API.
webdav definition
Now let's go back to hook_webdav_definitions. As you know now, it returns an associative array of path and definition. A definition can have following properties :
- "module" : This is the module owner of this path. Every path got a owner in order to resolve conflicts. If not specified, the module owner is the module in which the path is declared. So if you want to attach resources to some path your module don't own, you just have to specify the module owner of it. In the future, system will detect conflicts over path when two modules claim to be owner of the same path.
- "{operation} callback" : Required if this is a collection resource. The function to call to populate members for this path.
- "arguments" : An array of arguments to pass to the any call back function. Each element is the identifier given with % or %%.
- "access" : An array of perms.
Webdav operation callbacks
A callback is linked to its definition, so every callback will have the same first parameters following arguments definition attribute. So if we have this definition :
- '/node/%node_type/%node/%content_type'=>array(
- 'arguments'=>array('node'),
- 'members callback'=>'webdav_node_get_node_members',
- 'get callback'=>'webdav_node_content_get',
- 'access' => array (WEBDAV_NODE_PERMS_ACCESS),
- ),
The webdav_node_content_get callback will have $node as first parameter. This said, some callbacks can have more parameters. For example, for put, you'll have the $file_name containing the data to put.
get operation callback
This callback has only parameters defined in its webdav definition. The function can return a plain string or a file stream handler. If an error occurs, you have to user webdav_session_set_status with any HTTP error (ex. WEBDAV_HTTP_STATUS_FORBIDDEN).
- function webdav_node_node_content_get($node) {
- return $node->body;
- }
put operation callback
This callback have parameters defined in its webdav definition and a $file_name pointing to the file to put. If an error occurs, you have to user webdav_session_set_status with any HTTP error (ex. WEBDAV_HTTP_STATUS_FORBIDDEN).
- function webdav_node_node_content_put($node, $file_name) {
- // as node_save don't do any check, we should validate permissions here
- if (!node_access("update", $node)) {
- error_log("==".$node->nid);
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- return;
- }
- $content = file_get_contents($file_name);
- $node->body = $content;
- _webdav_node_save($node);
- }
create operation callback
This callback have parameters defined in its webdav definition and a $name for the new resource name to create. If an error occurs, you have to user webdav_session_set_status with any HTTP error (ex. WEBDAV_HTTP_STATUS_FORBIDDEN).
- function webdav_node_node_create ($node_type, $name) {
- // Create the new node
- global $user;
- $node=(object)array();
- $node->title=$name;
- $node->body=webdav_node_create_node_default_body();
- $node->log=webdav_node_create_node_default_log_message();
- $node->type=$node_type;
- $node->uid=$user->uid;
- $node->status=0;
-
- if (!node_access( "create", $node)) {
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- return;
- }
- node_save($node);
- }
delete operation callback
This callback has only the parameters defined in its webdav definition. If an error occurs, you have to user webdav_session_set_status with any HTTP error (ex. WEBDAV_HTTP_STATUS_FORBIDDEN).
- function webdav_node_node_delete($node) {
- if (!node_access( "delete", $node)) {
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- } else {
- node_delete($node->nid);
- }
- }
members operation callback
This callback has only the parameters defined in its webdav definition. It is used to get all members of a specified route. If an error occurs, you have to user webdav_session_set_status with any HTTP error (ex. WEBDAV_HTTP_STATUS_FORBIDDEN).
- function webdav_node_node_members($node) {
- $format=$node->format;
- $extension = webdav_node_extension_for_input_format($format);
- $mime_type = webdav_node_mime_type_for_input_format($format);
-
- $items[] = array (
- 'id' => "content.".$extension,
- 'name' => $node->title.".".$extension,
- 'created' => $node->created,
- 'changed' => $node->changed,
- 'size' => strlen($node->body),
- 'type' => $mime_type,
- 'encoding' => 'UTF8',
- );
- return $items;
- }
copy operation callback
This callback is a bit more tricky. As it involves a source and a target, and each two of them go a path definition with arguments, the callback go first the target definition parameters as arguments, and after the source definition arguments.
Second tricky part, the callback should be attached somewhere. So it will be to the target path definition.
If an error occurs, you have to user webdav_session_set_status with any HTTP error (ex. WEBDAV_HTTP_STATUS_FORBIDDEN).
- function webdav_node_webdav_copy(
- /* target path definition arguments */ $target_node_type,
- /* source path definition arguments */ $source_node) {
- if ($target_node_type!=$source_node->type) {
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- return;
- }
-
- // Create the new node
- global $user;
- $node=(object)array();
- $node->title=$name;
- $node->body=$source_node->body;
- $node->log=webdav_node_create_node_default_log_message();
- $node->type=$source_node->type;
- $node->uid=$user->uid;
- $node->status=0;
-
- // Check right
- if (!node_access( "create", $node)) {
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- return;
- }
- node_save($node);
- }
copy operation callback
It's working quite the same as copy but with a $new_name between the two arguments definition.
- function webdav_node_move($target_node_type, $new_name, $source_node) {
- if ($target_node_type!=$source_node->type) {
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- return;
- }
-
- // as node_save don't do any check, we should validate permissions here
- if (!node_access( "update", $source_node)) {
- webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
- return;
- }
- $source_node->title = $name;
- _webdav_node_save($source->parameters['node']);
- }