array("function" => "bloggerapi_newPost"), "blogger.editPost" => array("function" => "bloggerapi_editPost"), "blogger.getUsersBlogs" => array("function" => "bloggerapi_getUsersBlogs"), "blogger.getUserInfo" => array("function" => "bloggerapi_getUserInfo"), "blogger.getTemplate" => array("function" => "bloggerapi_getTemplate"), "blogger.setTemplate" => array("function" => "bloggerapi_setTemplate"), "blogger.getPost" => array("function" => "bloggerapi_getPost"), "blogger.getRecentPosts" => array("function" => "bloggerapi_getRecentPosts"), "blogger.deletePost" => array("function" => "bloggerapi_deletePost") ); } /* ** The following functions map to the various Blogger method calls. ** All these functions subsequently use the blogger_driver function */ function bloggerapi_newPost($params) { global $user, $xmlrpcerruser; /* ** Call the driver function with appropriate method to return a node */ $node = node_validate(bloggerapi_driver("newPost", $params, $error), $error); if (!$node->error) { if (node_access("create", $node)) { throttle("node", variable_get("max_node_rate", 900)); $nid = node_save($node); if ($nid) { watchdog("special", "$node->type: added '$node->title', via Blogger API"); return new xmlrpcresp(new xmlrpcval("$nid", "string")); } else { $error = bloggerapi_error("Error posting node"); return $error->error_resp; } } } return $node->error_resp; } function bloggerapi_editPost($params) { global $user, $xmlrpcerruser; $node = node_validate(bloggerapi_driver("editPost", $params, $error), $error); if (!$node->error) { $nid = node_save($node); if ($nid) { watchdog("special", "$node->type: updated '$node->title', via Blogger API"); return new xmlrpcresp(new xmlrpcval($nid, "string")); } else { # $error = bloggerapi_error("Error updating node: $node->nid"); return $node->error_resp; } } return $node->error_resp; } function bloggerapi_getUsersBlogs($params) { $resp = bloggerapi_driver("getUsersBlogs", $params); if (!$resp->error) { return new xmlrpcresp($resp); } else { return $resp->error_resp; } } function bloggerapi_getUserInfo($params) { $resp = bloggerapi_driver("getUserInfo", $params); if (!$resp->error) { return new xmlrpcresp($resp); } else { return $resp->error_resp; } } function bloggerapi_getTemplate($params) { $error = bloggerapi_error(t("Get Template is not relevant for Drupal")); return $error->error_resp; } function bloggerapi_setTemplate($params) { $error = bloggerapi_error(t("Set Template is not relevant for Drupal")); return $error->error_resp; } function bloggerapi_getPost($params) { $resp = bloggerapi_driver("getPost", $params); if (!$resp->error) { return new xmlrpcresp($resp); } else { return $resp->error_resp; } } function bloggerapi_getRecentPosts($params) { $resp = bloggerapi_driver("getRecentPosts", $params); if (!$resp->error) { return new xmlrpcresp($resp); } else { return $resp->error_resp; } } function bloggerapi_deletePost($params) { $resp = bloggerapi_driver("deletePost", $params); if (!$resp->error) { return new xmlrpcresp($resp); } else { return $resp->error_resp; } } function bloggerapi_driver($method, $params = 0, $error = 0) { global $user, $xmlrpcusererr; /* ** Convert parameters to an array */ $cparams = bloggerapi_convert($params); /* ** Validate the user, the postion in the array for username and password ** are not always the same. */ if ($method == "getUsersBlogs" || $method == "getUserInfo") { $user = bloggerapi_validate_user($cparams[1], $cparams[2]); } else { $user = bloggerapi_validate_user($cparams[2], $cparams[3]); } if ($user->uid && user_access("access Blogger API")) { /* ** Check for title tags, if not generate one. */ if (eregi("(.*)", $cparams[4], $title)) { $title = strip_tags($title[0]); $cparams[4] = ereg_replace(".*", "", $cparams[4]); } else { $title = bloggerapi_title($cparams[4]); } /* ** Maps params to node array or call another function */ switch ($method) { case "newPost": return array("type" => "blog", "name" => $user->name, "title" => $title, "body" => $cparams[4], "status" => 1, "moderate" => 0, "comment" => 2, "promote" => 0, "revision" => 0); break; case "editPost": $node = node_load(array("nid" => $cparams[1])); if ($node->uid == $user->uid) { return array("nid" => $cparams[1], "type" => "blog", "name" => $user->name, "title" => $title, "body" => $cparams[4], "status" => 1, "moderate" => 0, "comment" => 2, "promote" => 0, "revision" => 0); } else { return bloggerapi_error("Error updating node"); } break; case "getUsersBlogs": return bloggerapi_user_blogs(); break; case "getUserInfo": return bloggerapi_user_info(); break; case "getPost": return bloggerapi_node_load($cparams[1]); break; case "getRecentPosts": return bloggerapi_node_recent($cparams[4]); break; case "deletePost": return bloggerapi_node_delete($cparams[1]); break; case "distribute": break; default: return bloggerapi_error("Error in the authentication process"); break; } } else { return bloggerapi_error("Error in the authentication process"); } } /* ** The following functions do the *actual* work of deleting posts ** and returning posts etc, */ function bloggerapi_user_blogs() { global $user; if ($user->uid) { $struct = new xmlrpcval(array("url" => new xmlrpcval(url("blog/$user->uid")), "blogid" => new xmlrpcval($user->uid), "blogName" => new xmlrpcval($user->name . "'s blog at ". variable_get("site_name", "drupal"))),"struct"); return new xmlrpcval(array($struct), "array"); } else { return bloggerapi_error("Error retrieving user blogs"); } } function bloggerapi_user_info() { global $user; if ($user->uid) { return new xmlrpcval(array("nickname" => new xmlrpcval($user->name, "string"), "userid" => new xmlrpcval($user->id, "string"), "url" => new xmlrpcval(url("blog/view/". urlencode($user->uid)), "string"), "email" => new xmlrpcval($user->mail, "string"), "lastname" => new xmlrpcval(substr($user->name, strrpos($user->name," ")+1), "string"), "firstname" => new xmlrpcval(substr($user->name, 0, strrpos($user->name," ")), "string"), ), "struct"); } else { return bloggerapi_error("Error retrieving user information"); } } function bloggerapi_node_load($nid) { global $user; $blog = node_load(array("nid" => $nid)); if ($blog->uid == $user->uid) { $body = "$blog->title\n". $blog->body; return new xmlrpcval(array("userid" => new xmlrpcval($user->name, "string"), "dateCreated" => new xmlrpcval(iso8601_encode($blog->timestamp),"dateTime.iso8601"), "content" => new xmlrpcval($body,"string"), "postid" => new xmlrpcval($blog->nid,"string") ), "struct"); } else { return bloggerapi_error("Error retrieving blogid: $nid"); } } function bloggerapi_node_recent($num) { global $user; if (($num == 0) or ($num > 100)) $num = 50; $result = db_query_range("SELECT n.*, u.name FROM node n LEFT JOIN users u ON n.uid = u.uid WHERE n.uid = %d ORDER BY n.nid DESC", $user->uid, 0, $num); if ($result) { while ($blog = db_fetch_object($result)) { $body = "$blog->title\n". $blog->body; $blogs[] = new xmlrpcval(array("userid" => new xmlrpcval($blog->name,"string"), "dateCreated" => new xmlrpcval(iso8601_encode($blog->created),"dateTime.iso8601"), "content" => new xmlrpcval($body,"string"), "postid" => new xmlrpcval($blog->nid,"string") ), "struct"); } return new xmlrpcval($blogs, "array"); } else { return bloggerapi_error("Error retrieving recent blogs"); } } //TODO: rework node_delete() and then invoke it instead of performing the deletion here. function bloggerapi_node_delete($nid) { global $user; $node = node_load(array("nid" => $nid)); if ($node->uid == $user->uid) { if (node_access("delete", $node)) { /* ** Delete the specified node and its comments: */ db_query("DELETE FROM node WHERE nid = '$node->nid'"); db_query("DELETE FROM comments WHERE nid = '$node->nid'"); watchdog("special", "$node->type: deleted '$node->title', via Blogger API"); $message = "Node: $node->nid, was deleted"; return new xmlrpcval($message, "string"); } } else { return bloggerapi_error("Error deleting node: $nid"); } } /* ** Helper functions */ function tt(){ $tt = array_flip(get_html_translation_table(HTML_ENTITIES)); $tt["'"] = "'"; return $tt; } function bloggerapi_error($message) { global $xmlrpcusererr; $error->error = 1; $error->error_msg = $message; $error->error_resp = new xmlrpcresp(0, $xmlrpcerruser+1, $message); return $error; } function bloggerapi_validate_user($username, $password) { global $user; if ($username && $password) { $user = user_load(array("name" => $username, "pass" => $password, "status" => 1)); return $user; } else { return 0; } } function bloggerapi_convert($params) { $cparams= array(); $num_params = $params->getNumParams(); for ($i = 0; $i < $num_params; $i++) { $sn = $params->getParam($i); $cparams[] = $sn->scalarval(); } return $cparams; } function bloggerapi_title($body) { $title = strip_tags(strtr($title ? $title : substr(strip_tags(strtr($body, tt() )), 0, 30), tt() )); return $title; } /* ** Admin functions and module hooks */ function bloggerapi_perm() { return array("access Blogger API"); } function bloggerapi_system($field){ $system["description"] = t("Enables users to post using tools or applications that support the Blogger API."); return $system[$field]; } function bloggerapi_help() { $output = "

Introduction

"; $output .= "

Blogger, the well-known public weblog service, provides an application programing interface (API) to allow remote procedure calls (RPC) to the Blogger service. Drupal supports this Blogger API, which means that many remote clients (e.g. Radio, Blogbuddy, w.bloggar, TextRouter, may post to Drupal. These clients provide a bevy of interesting capabilities like offline composing, spellcheck, and WYSIWYG editing; many folks prefer to blog with a client application over typical web forms. By supporting the Blogger API, Drupal grows grander than a web site engine, it's a content accepting machine™.

"; $output .= "

The Blogger API uses the XML-RPC protocol for communicating with the outside world. XML-RPC, originally developed by Dave Winer of UserLand Software, is a simple XML-based RPC specification ideally suited to the web. Drupal also uses XML-RPC for several other tasks (e.g. notifiying weblogs.com of blog updates and making/accepting ". l("distributed authentication", "user/help") ." requests)

"; $output .= "

Blogger API implementation

"; $output .= "

A word of warning on the Blogger API: it is unofficial. It exists because Blogger is one of the most popular services and also they were first to implement an XML-RPC interface to their service. It may not be the best implementation of a distributed weblog API. For a promising candidate, see the Echo project.

"; $output .= "

Drupal's support for the Blogger API is quite complete. Each method with an asterisk below has been implemented in Drupal.

"; $output .= "

blogger.newPost()*
blogger.editPost()*
blogger.getUsersBlogs()*
blogger.getUserInfo()*
blogger.getTemplate()
blogger.setTemplate()

"; $output .= "

Drupal also supports the following methods. These methods were added after the those listed above and are not documented on the Blogger API web site. Each method is linked to its corresponding blogger-dev mailing list posts:

"; $output .= "

blogger.getPost()*
blogger.getRecentPosts()*
blogger.deletePost()*

"; $output .= "

Installation and usage

"; $output .= "

To install the Blogger API module, enable the module on the ". l("modules configuration page", "admin/systems/modules") ." Also make sure you have your permissions set correctly for accessing the Blogger API, the relevant settings can be found under the ". l("user management", "admin/user/permission") ." section in the administration pages. Check the checkbox behind the line \"access Blogger API\" for the roles that are allowed to use the Blogger API.

"; $output .= "

Once the API is enabled you can download one of the above mentioned Blogger API clients and get blogging.

"; $output .= "

Setup of the client

"; $output .= "

The Drupal page you need to call in order to connect using the Blogger API is http://server/xmlrpc.php where server is the URL of the site you want to post to. As an example when posting to drupal.org, the account settings for w.bloggar would be: host: www.drupal.org (default = plant.blogger.com) and page: xmlrpc.php (default = /api/RPC2).

"; $output .= "

You can't use remote authentication when posting using a Blogger API enabled client, even when you could use that to authenticate on the site itself. You will have to use the site's local username, enter a password for that account, and then use that combination to post using the Blogger API client.

"; $output .= "

Notes and limitations

"; $output .= ""; return t($output); } ?>