'Blogger API', '%metaweblogAPI' => 'MetaWeblog API', '%moveabletype' => 'Moveable Type API')); case 'admin/modules#description': return t('Enable users to post using applications that support XML-RPC blog APIs'); } } /** * Implementation of hook_xmlrpc(). */ function blogapi_xmlrpc() { $methods = array('blogger.getUsersBlogs' => array('function' => 'blogapi_get_users_blogs'), 'blogger.getUserInfo' => array('function' => 'blogapi_get_user_info'), 'blogger.newPost' => array('function' => 'blogapi_new_post'), 'blogger.editPost' => array('function' => 'blogapi_edit_post'), 'blogger.deletePost' => array('function' => 'blogapi_delete_post'), 'blogger.getRecentPosts' => array('function' => 'blogapi_get_recent_posts'), 'metaWeblog.newPost' => array('function' => 'blogapi_new_post'), 'metaWeblog.editPost' => array('function' => 'blogapi_edit_post'), 'metaWeblog.getPost' => array('function' => 'blogapi_get_post'), 'metaWeblog.newMediaObject' => array('function' => 'blogapi_new_media_object'), 'metaWeblog.getCategories' => array('function' => 'blogapi_get_category_list'), 'metaWeblog.getRecentPosts' => array('function' => 'blogapi_get_recent_posts'), 'mt.getRecentPostTitles' => array('function' => 'blogapi_get_recent_post_titles'), 'mt.getCategoryList' => array('function' => 'blogapi_get_category_list'), 'mt.getPostCategories' => array('function' => 'blogapi_get_post_categories'), 'mt.setPostCategories' => array('function' => 'blogapi_set_post_categories') ); return $methods; } /** * Blogging API callback. Finds the URL of a user's blog. */ function blogapi_get_users_blogs($req_params) { $params = blogapi_convert($req_params); // Remove unused appkey from bloggerAPI. if (count($params) == 6) { $params = array_slice($params, 1); } $user = blogapi_validate_user($params[1], $params[2]); if ($user->uid) { $struct = new xmlrpcval(array('url' => new xmlrpcval(url('blog/' . $user->uid, NULL, NULL, true)), 'blogid' => new xmlrpcval($user->uid), 'blogName' => new xmlrpcval($user->name . "'s blog")), 'struct'); $resp = new xmlrpcval(array($struct), 'array'); return new xmlrpcresp($resp); } else { return blogapi_error($user); } } /** * Blogging API callback. Returns profile information about a user. */ function blogapi_get_user_info($req_params) { $params = blogapi_convert($req_params); $user = blogapi_validate_user($params[1], $params[2]); if ($user->uid) { $name = explode(' ', $user->realname ? $user->realname : $user->name, 2); $struct = new xmlrpcval(array('userid' => new xmlrpcval($user->uid, 'string'), 'lastname' => new xmlrpcval($name[1], 'string'), 'firstname' => new xmlrpcval($name[0], 'string'), 'nickname' => new xmlrpcval($user->name, 'string'), 'email' => new xmlrpcval($user->mail, 'string'), 'url' => new xmlrpcval(url('blog/view/' . $user->uid, NULL, NULL, true), 'string')), 'struct'); return new xmlrpcresp($struct); } else { return blogapi_error($user); } } /** * Blogging API callback. Inserts a new blog post as a node. */ function blogapi_new_post($req_params) { $params = blogapi_convert($req_params); // Remove unused appkey from bloggerAPI. if (count($params) == 6) { $params = array_slice($params, 1); } $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $promote = variable_get('node_promote_blog', 0); $comment = variable_get('node_comment_blog', 2); $moderate = variable_get('node_moderate_blog', 0); $revision = variable_get('node_revision_blog', 0); // check for bloggerAPI vs. metaWeblogAPI if (is_array($params[3])) { $title = $params[3]['title']; $body = $params[3]['description']; } else { $title = blogapi_blogger_title($params[3]); $body = $params[3]; } if (!valid_input_data($title, $body)) { return blogapi_error(t('Terminated request because of suspicious input data.')); } $node = node_validate(array('type' => 'blog', 'uid' => $user->uid, 'name' => $user->name, 'title' => $title, 'body' => $body, 'status' => $params[4], 'promote' => $promote, 'comment' => $comment, 'moderate' => $moderate, 'revision' => $revision )); if (form_get_errors()) { return blogapi_error(); } if (!node_access('create', $node)) { return blogapi_error(message_access()); } $nid = node_save($node); if ($nid) { watchdog('special', t('%node-type: added "%node-title" using blog API', array('%node-type' => t("$node->type"), '%node-title' => $node->title)), l(t('view'), "node/$nid")); return new xmlrpcresp(new xmlrpcval($nid, 'string')); } return blogapi_error(t('error storing post')); } /** * Blogging API callback. Modifies the specified blog node. */ function blogapi_edit_post($req_params) { $params = blogapi_convert($req_params); if (count($params) == 6) { $params = array_slice($params, 1); } $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $node = node_load(array('nid' => $params[0])); if (!$node) { return blogapi_error(message_na()); } if (!node_access('update', $node)) { return blogapi_error(message_access()); } // check for bloggerAPI vs. metaWeblogAPI if (is_array($params[3])) { $title = $params[3]['title']; $body = $params[3]['description']; } else { $title = blogapi_blogger_title($params[3]); $body = $params[3]; } if (!valid_input_data($title, $body)) { return blogapi_error(t('Terminated request because of suspicious input data.')); } $node->title = $title; $node->body = $body; $node->status = $params[4]; $node = node_validate($node); if (form_get_errors()) { return blogapi_error(); } $terms = module_invoke('taxonomy', 'node_get_terms', $node->nid, 'tid'); foreach ($terms as $term) { $node->taxonomy[] = $term->tid; } $nid = node_save($node); if ($nid) { watchdog('special', t('%node-type: updated "%node-title" using blog API', array('%node-type' => t("$node->type"), '%node-title' => $node->title)), l(t('view'), "node/$nid")); return new xmlrpcresp(new xmlrpcval(true, 'boolean')); } return blogapi_error(t('error storing node')); } /** * Blogging API callback. Returns a specified blog node. */ function blogapi_get_post($req_params) { $params = blogapi_convert($req_params); $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $node = node_load(array('nid' => $params[0])); $blog = new xmlrpcval(array('userid' => new xmlrpcval($node->name, 'string'), 'dateCreated' => new xmlrpcval(iso8601_encode($node->created), 'dateTime.iso8601'), 'title' => new xmlrpcval($node->title, 'string'), 'description' => new xmlrpcval($node->body, 'string'), 'postid' => new xmlrpcval($node->nid, 'string')), 'struct'); return new xmlrpcresp($blog); } /** * Blogging API callback. Removes the specified blog node. */ function blogapi_delete_post($req_params) { $params = blogapi_convert($req_params); $user = blogapi_validate_user($params[2], $params[3]); if (!$user->uid) { return blogapi_error($user); } $ret = node_delete(array('nid' => $params[1], 'confirm' => 1)); return new xmlrpcresp(new xmlrpcval(true, 'boolean')); } /** * Blogging API callback. Inserts a file into Drupal. */ function blogapi_new_media_object($req_params) { $params = blogapi_convert($req_params); $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $name = basename($params[3]['name']); $data = $params[3]['bits']; if (!$data) { return blogapi_error(t('No file sent')); } if (!$file = file_save_data($data, $name)) { return blogapi_error(t('Error storing file')); } // Return the successful result. $result = new xmlrpcval(array('url' => new xmlrpcval(file_create_url($file), 'string')), 'struct'); return new xmlrpcresp($result); } /** * Blogging API callback. Returns a list of the taxonomy terms that can be * associated with a blog node. */ function blogapi_get_category_list($req_params) { $vocabularies = module_invoke('taxonomy', 'get_vocabularies', 'blog', 'vid'); $categories = array(); if ($vocabularies) { foreach ($vocabularies as $vocabulary) { $terms = module_invoke('taxonomy', 'get_tree', $vocabulary->vid, 0, -1); foreach ($terms as $term) { $term_name = $term->name; foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) { $term_name = $parent->name . '/' . $term_name; } $categories[] = new xmlrpcval(array('categoryName' => new xmlrpcval($term_name, 'string'), 'categoryId' => new xmlrpcval($term->tid, 'string')), 'struct'); } } } return new xmlrpcresp(new xmlrpcval($categories, 'array')); } /** * Blogging API callback. Returns a list of the taxonomy terms that are * assigned to a particular node. */ function blogapi_get_post_categories($req_params) { $params = blogapi_convert($req_params); $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $terms = module_invoke('taxonomy', 'node_get_terms', $params[0], 'tid'); $categories = array(); foreach ($terms as $term) { $term_name = $term->name; foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) { $term_name = $parent->name . '/' . $term_name; } $categories[] = new xmlrpcval(array('categoryName' => new xmlrpcval($term_name, 'string'), 'categoryId' => new xmlrpcval($term->tid, 'string'), 'isPrimary' => new xmlrpcval(true, 'boolean')), 'struct'); } return new xmlrpcresp(new xmlrpcval($categories, 'array')); } /** * Blogging API callback. Assigns taxonomy terms to a particular node. */ function blogapi_set_post_categories($req_params) { $params = blogapi_convert($req_params); $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $nid = $params[0]; $terms = array(); foreach ($params[3] as $category) { $terms[] = $category['categoryId']->scalarval(); } module_invoke('taxonomy', 'node_save', $nid, $terms); return new xmlrpcresp(new xmlrpcval(true, 'boolean')); } /** * Blogging API callback. Returns the latest few postings in a user's blog. $bodies TRUE * * returns a bandwidth-friendly list. */ function blogapi_get_recent_posts($req_params, $bodies = TRUE) { $params = blogapi_convert($req_params); // Remove unused appkey (from bloggerAPI). if (count($params) == 5) { $params = array_slice($params, 1); } $user = blogapi_validate_user($params[1], $params[2]); if (!$user->uid) { return blogapi_error($user); } $result = db_query_range('SELECT n.nid, n.title,'. ($bodies ? ' n.body,' : '') ." n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = 'blog' AND n.uid = %d ORDER BY n.created DESC", $user->uid, 0, $params[3]); while ($blog = db_fetch_object($result)) { $xmlrpcval = array ( 'userid' => new xmlrpcval($blog->name, 'string'), 'dateCreated' => new xmlrpcval(iso8601_encode($blog->created), 'dateTime.iso8601'), 'title' => new xmlrpcval($blog->title, 'string'), 'postid' => new xmlrpcval($blog->nid, 'string') ); if ($bodies) { $xmlrpcval['content'] = new xmlrpcval("$blog->title$blog->body", 'string'); $xmlrpcval['description'] = new xmlrpcval($blog->body, 'string'); } $blogs[] = new xmlrpcval($xmlrpcval, 'struct'); } return new xmlrpcresp(new xmlrpcval($blogs, 'array')); } // see above function blogapi_get_recent_post_titles($req_params) { return blogapi_get_recent_posts($req_params, TRUE); } /** * Process the parameters to an XMLRPC callback, and return them as an array. */ function blogapi_convert($params) { $cparams = array(); $num_params= $params->getNumParams(); for ($i = 0; $i < $num_params; $i++) { $sn = $params->getParam($i); $cparams[] = $sn->getval(); } return $cparams; } /** * Prepare an error message for returning to the XMLRPC caller. */ function blogapi_error($message) { global $xmlrpcusererr; if (!is_array($message)) { $message = array($message); } if ($errors = form_get_errors()) { $message = $message + $errors; } $message = implode(' ', $message); return new xmlrpcresp(0, $xmlrpcusererr + 1, strip_tags($message)); } /** * Ensure that the given user has permission to edit a blog. */ function blogapi_validate_user($username, $password) { global $user; $user = user_load(array('name' => $username, 'pass' => $password, 'status' => 1)); if ($user->uid) { if (user_access('edit own blog')) { return $user; } else { return message_access(); } } else { return t('Wrong username or password.'); } } /** * For the blogger API, extract the node title from the contents field. */ function blogapi_blogger_title(&$contents) { if (eregi('([^<]*)', $contents, $title)) { $title = strip_tags($title[0]); $contents = ereg_replace('[^<]*', '', $contents); } else { list($title, $rest) = explode("\n", $contents, 2); } return $title; } function blogapi_settings() { $output = form_select(t('XML-RPC Engine'), 'blogapi_engine', variable_get('blogapi_engine', 0), array(0 => 'Blogger', 1 => 'MetaWeblog', 2 => 'Movabletype'), t('RSD or Really-Simple-Discovery is a mechanism which allows external blogger tools to discover the APIs they can use to interact with Drupal. The common XML-RPC engines are Blogger, MetaWeblog and Movabletype. If you are not sure which is the correct setting, choose Blogger.')); return $output; } function blogapi_menu() { global $user; $items = array(); if ($_GET['q'] == variable_get('site_frontpage', 'node')) { drupal_set_html_head(''); } $items[] = array('path' => 'blogapi', 'title' => t('RSD'), 'callback' => 'blogapi_blogapi', 'access' => user_access('access_content'), 'type' => MENU_CALLBACK); return $items; } function blogapi_blogapi() { switch (arg(1)) { case 'rsd': blogapi_rsd(); break; default: drupal_not_found(); break; } } function blogapi_rsd() { global $base_url; $xmlrpc = $base_url . FILE_SEPARATOR . 'xmlrpc.php'; $base = url('', NULL, NULL, TRUE); $blogid = 1; # until we figure out how to handle multiple bloggers $metaweblog = 'false'; $blogger = 'false'; $mt = 'false'; if (variable_get('blogapi_engine', 0) == 0) { $blogger = 'true'; } else if (variable_get('blogapi_engine', 0) == 1) { $metaweblog = 'true'; } else if (variable_get('blogapi_engine', 0) == 2) { $mt = 'true'; } print <<<__RSD__ Drupal http://www.drupal.org/ $base __RSD__; } ?>