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 .= "- The Blogger API contains an AppKey that is discarded in the Drupal Implementation.
- The Blogger API does not allow for a title element. Our work around for this is either to use <title></title> tags in the body of your post or let the module create a title by inspecting the first few lines of the post body.
- The publish parameter is always set to 1.
- When using the getUserInfo call, Drupal attempts to generate a first and last name from the Drupal username; no distinction is made internally
- GetUsersBlogs only returns one blog because unlike Blogger, Drupal only allows one blog per user.
";
break;
case 'admin/system/modules':
$output .= "Enables users to post using tools or applications that support the Blogger API.";
break;
}
return t($output);
}
?>