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); return $resp->error ? $resp->error_resp : new xmlrpcresp($resp); } function bloggerapi_getUserInfo($params) { $resp = bloggerapi_driver("getUserInfo", $params); return $resp->error ? $resp->error_resp : new xmlrpcresp($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); return $resp->error ? $resp->error_resp : new xmlrpcresp($resp); } function bloggerapi_getRecentPosts($params) { $resp = bloggerapi_driver("getRecentPosts", $params); return $resp->error ? $resp->error_resp : new xmlrpcresp($resp); } function bloggerapi_deletePost($params) { $resp = bloggerapi_driver("deletePost", $params); return $resp->error ? $resp->error_resp : new xmlrpcresp($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", "uid" => $user->uid, "name" => $user->name, "title" => $title, "body" => $cparams[4], "status" => 1, "moderate" => 0, "comment" => 2, "promote" => 0, "revision" => 0); 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"); } case "getUsersBlogs": return bloggerapi_user_blogs(); case "getUserInfo": return bloggerapi_user_info(); case "getPost": return bloggerapi_node_load($cparams[1]); case "getRecentPosts": return bloggerapi_node_recent($cparams[4]); case "deletePost": return bloggerapi_node_delete($cparams[1]); case "distribute": break; default: return bloggerapi_error("Error in the authentication process"); } } 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 INNER 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){ $output = ""; if ($field == "description") {$output .= bloggerapi_help("admin/system/modules"); }; return $output; } function bloggerapi_help($section) { $output = ""; switch ($section) { case 'admin/bloggerapi/help': case 'admin/help': $output = "

Introduction

"; $output .= strtr("

%blogger-com, 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. %client-radio, %client-blogbuddy, %client-w_bloggar, and %client-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™.

", array("%blogger-com" => "". t("Blogger") ."", "%blogger-api" => "". t("Blogger API") ."", "%client-radio" => "". t("Radio") ."", "%client-blogbuddy" => "". t("BlogBuddy") ."", "%client-w_bloggar" => "". t("w.bloggar") ."", "%client-textrouter" => "". t("TextRouter") ."" )); $output .= strtr("

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 %dist-auth requests)

",array("%blogger-api" => "". t("Blogger API") ."", "%xml-rpc" => "". t("XML-RPC") ."", "%userland-software" => "". t("UserLand Software") ."", "%weblogs-com" => "". t("weblogs.com") ."", "%dist-auth" => l(t("distributed authentication"), "user/help") )) ; $output .= "

Blogger API implementation

"; $output .= strtr("

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

", array("%echo-proj" => "". t("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 .= strtr("

%bloggerapi-newpost
%bloggerapi-editpost
%bloggerapi-getuserblogs
%bloggerapi-getuserinfo
%bloggerapi-gettemplate
%bloggerapi-settemplate

", array("%bloggerapi-newpost" => "%blogger-api/xmlrpc_newPost.html\">". t("blogger.newPost()*") ."", "%bloggerapi-editpost" => "%blogger-api/xmlrpc_editPost.html\">". t("blogger.editPost()*") ."", "%bloggerapi-getuserblogs" => "%blogger-api/xmlrpc_getUserBlogs.html\">". t("blogger.getUsersBlogs()*") ."", "%bloggerapi-getuserinfo" => "%blogger-api/xmlrpc_getUserInfo.html\">". t("blogger.getUserInfo()*") ."", "%bloggerapi-gettemplate" => "%blogger-api/xmlrpc_getTemplate.html\">". t("blogger.getTemplate()*") ."", "%bloggerapi-settemplate" => "blogger-api/xmlrpc_setTemplate.html\">". t("blogger.setTemplate()*") ."" )); $output = strtr($output, array("%blogger-api" => "%mess-296
%mess-225
%mess-147

", array("%mess-296" => "%blogger-dev/296\">". t("blogger.getPost()*") ."
", "%mess-225" => "%blogger-dev/225\">". t("blogger.getRecentPosts()*") ."", "%mess-147" => "%blogger-dev/147\">". t("blogger.deletePost()*") ."" )); $output = strtr($output, array("%blogger-dev" => "To install the Blogger API module, enable the module on the %mod-config. Also make sure you have your permissions set correctly for accessing the Blogger API, the relevant settings can be found under the %user-management 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.

", array("%mod-config" => l(t("modules configuration page"), "admin/systems/modules"), "%user-management" => l(t("user management"), "admin/user/permission") )); $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 .= strtr("

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 %client-wbloggar would be: host: www.drupal.org (default = plant.blogger.com) and page: xmlrpc.php (default = /api/RPC2).

", array("%client-wbloggar" => "
". t("w.bloggar") ."" )); $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 .= ""; break; case 'admin/system/modules': $output .= "Enables users to post using tools or applications that support the Blogger API."; break; } return t($output); } ?>