Code coverage for /20080809/modules/blogapi/blogapi.module

Line #Times calledCode
1
<?php
2
// $Id: blogapi.module,v 1.120 2008/07/23 07:42:08 dries Exp $
3
4
/**
5
 * @file
6
 * Enable users to post using applications that support XML-RPC blog APIs.
7
 */
8
9
/**
10
 * Implementation of hook_help().
11
 */
1225
function blogapi_help($path, $arg) {
13
  switch ($path) {
1411
    case 'admin/help#blogapi':
150
      $output = '<p>' . t("The Blog API module allows your site's users to
access and post to their blogs from external blogging clients. External
blogging clients are available for a wide range of desktop operating
systems, and generally provide a feature-rich graphical environment for
creating and editing posts.") . '</p>';
160
      $output .= '<p>' . t('<a href="@ecto-link">Ecto</a>, a blogging
client available for both Mac OS X and Microsoft Windows, can be used with
Blog API. Blog API also supports <a href="@blogger-api">Blogger API</a>, <a
href="@metaweblog-api">MetaWeblog API</a>, and most of the <a
href="@movabletype-api">Movable Type API</a>. Blogging clients and other
services (e.g. <a href="@flickr">Flickr\'s</a> "post to blog") that support
these APIs may also be compatible.', array('@ecto-link' =>
url('http://infinite-sushi.com/software/ecto/'), '@blogger-api' =>
url('http://www.blogger.com/developers/api/1_docs/'), '@metaweblog-api' =>
url('http://www.xmlrpc.com/metaWeblogApi'), '@movabletype-api' =>
url('http://www.movabletype.org/docs/mtmanual_programmatic.html'),
'@flickr' => url('http://www.flickr.com'))) . '</p>';
170
      $output .= '<p>' . t('Select the content types available to external
clients on the <a href="@blogapi-settings">Blog API settings page</a>. If
supported and available, each content type will be displayed as a separate
"blog" by the external client.', array('@blogapi-settings' =>
url('admin/settings/blogapi'))) . '</p>';
180
      $output .= '<p>' . t('For more information, see the online handbook
entry for <a href="@blogapi">Blog API module</a>.', array('@blogapi' =>
url('http://drupal.org/handbook/modules/blogapi/'))) . '</p>';
190
      return $output;
200
  }
2111
}
22
23
/**
24
 * Implementation of hook_perm().
25
 */
2625
function blogapi_perm() {
27
  return array(
281
    'administer content with blog api' => t('Manage website content from
external tools.'),
291
  );
300
}
31
32
/**
33
 * Implementation of hook_xmlrpc().
34
 */
3525
function blogapi_xmlrpc() {
36
  return array(
37
    array(
388
      'blogger.getUsersBlogs',
398
      'blogapi_blogger_get_users_blogs',
408
      array('array', 'string', 'string', 'string'),
418
      t('Returns a list of blogs to which an author has posting
privileges.')),
42
    array(
438
      'blogger.getUserInfo',
448
      'blogapi_blogger_get_user_info',
458
      array('struct', 'string', 'string', 'string'),
468
      t('Returns information about an author in the system.')),
47
    array(
488
      'blogger.newPost',
498
      'blogapi_blogger_new_post',
508
      array('string', 'string', 'string', 'string', 'string', 'string',
'boolean'),
518
      t('Creates a new post, and optionally publishes it.')),
52
    array(
538
      'blogger.editPost',
548
      'blogapi_blogger_edit_post',
558
      array('boolean', 'string', 'string', 'string', 'string', 'string',
'boolean'),
568
      t('Updates the information about an existing post.')),
57
    array(
588
      'blogger.getPost',
598
      'blogapi_blogger_get_post',
608
      array('struct', 'string', 'string', 'string', 'string'),
618
      t('Returns information about a specific post.')),
62
    array(
638
      'blogger.deletePost',
648
      'blogapi_blogger_delete_post',
658
      array('boolean', 'string', 'string', 'string', 'string', 'boolean'),
668
      t('Deletes a post.')),
67
    array(
688
      'blogger.getRecentPosts',
698
      'blogapi_blogger_get_recent_posts',
708
      array('array', 'string', 'string', 'string', 'string', 'int'),
718
      t('Returns a list of the most recent posts in the system.')),
72
    array(
738
      'metaWeblog.newPost',
748
      'blogapi_metaweblog_new_post',
758
      array('string', 'string', 'string', 'string', 'struct', 'boolean'),
768
      t('Creates a new post, and optionally publishes it.')),
77
    array(
788
      'metaWeblog.editPost',
798
      'blogapi_metaweblog_edit_post',
808
      array('boolean', 'string', 'string', 'string', 'struct', 'boolean'),
818
      t('Updates information about an existing post.')),
82
    array(
838
      'metaWeblog.getPost',
848
      'blogapi_metaweblog_get_post',
858
      array('struct', 'string', 'string', 'string'),
868
      t('Returns information about a specific post.')),
87
    array(
888
      'metaWeblog.newMediaObject',
898
      'blogapi_metaweblog_new_media_object',
908
      array('string', 'string', 'string', 'string', 'struct'),
918
      t('Uploads a file to your webserver.')),
92
    array(
938
      'metaWeblog.getCategories',
948
      'blogapi_metaweblog_get_category_list',
958
      array('struct', 'string', 'string', 'string'),
968
      t('Returns a list of all categories to which the post is
assigned.')),
97
    array(
988
      'metaWeblog.getRecentPosts',
998
      'blogapi_metaweblog_get_recent_posts',
1008
      array('array', 'string', 'string', 'string', 'int'),
1018
      t('Returns a list of the most recent posts in the system.')),
102
    array(
1038
      'mt.getRecentPostTitles',
1048
      'blogapi_mt_get_recent_post_titles',
1058
      array('array', 'string', 'string', 'string', 'int'),
1068
      t('Returns a bandwidth-friendly list of the most recent posts in the
system.')),
107
    array(
1088
      'mt.getCategoryList',
1098
      'blogapi_mt_get_category_list',
1108
      array('array', 'string', 'string', 'string'),
1118
      t('Returns a list of all categories defined in the blog.')),
112
    array(
1138
      'mt.getPostCategories',
1148
      'blogapi_mt_get_post_categories',
1158
      array('array', 'string', 'string', 'string'),
1168
      t('Returns a list of all categories to which the post is
assigned.')),
117
    array(
1188
      'mt.setPostCategories',
1198
      'blogapi_mt_set_post_categories',
1208
      array('boolean', 'string', 'string', 'string', 'array'),
1218
      t('Sets the categories for a post.')),
122
    array(
1238
      'mt.supportedMethods',
1248
      'xmlrpc_server_list_methods',
1258
      array('array'),
1268
      t('Retrieve information about the XML-RPC methods supported by the
server.')),
127
    array(
1288
      'mt.supportedTextFilters',
1298
      'blogapi_mt_supported_text_filters',
1308
      array('array'),
1318
      t('Retrieve information about the text formatting plugins supported
by the server.')),
132
    array(
1338
      'mt.publishPost',
1348
      'blogapi_mt_publish_post',
1358
      array('boolean', 'string', 'string', 'string'),
1368
      t('Publish (rebuild) all of the static files related to an entry from
your blog. Equivalent to saving an entry in the system (but without the
ping).')));
1370
}
138
139
/**
140
 * Blogging API callback. Finds the URL of a user's blog.
141
 */
14225
function blogapi_blogger_get_users_blogs($appid, $username, $password) {
1431
  $user = blogapi_validate_user($username, $password);
1441
  if ($user->uid) {
1451
    $types = _blogapi_get_node_types();
1461
    $structs = array();
1471
    foreach ($types as $type) {
1481
      $structs[] = array('url' => url('blog/' . $user->uid,
array('absolute' => TRUE)), 'blogid' => $type, 'blogName' => $user->name .
": " . $type);
1491
    }
150
1511
    return $structs;
1520
  }
153
  else {
1540
    return blogapi_error($user);
155
  }
1560
}
157
158
/**
159
 * Blogging API callback. Returns profile information about a user.
160
 */
16125
function blogapi_blogger_get_user_info($appkey, $username, $password) {
1620
  $user = blogapi_validate_user($username, $password);
163
1640
  if ($user->uid) {
1650
    $name = explode(' ', $user->realname ? $user->realname : $user->name,
2);
166
    return array(
1670
      'userid' => $user->uid,
1680
      'lastname' => $name[1],
1690
      'firstname' => $name[0],
1700
      'nickname' => $user->name,
1710
      'email' => $user->mail,
1720
      'url' => url('blog/' . $user->uid, array('absolute' => TRUE)));
1730
  }
174
  else {
1750
    return blogapi_error($user);
176
  }
1770
}
178
179
/**
180
 * Blogging API callback. Inserts a new blog post as a node.
181
 */
18225
function blogapi_blogger_new_post($appkey, $blogid, $username, $password,
$content, $publish) {
1831
  $user = blogapi_validate_user($username, $password);
1841
  if (!$user->uid) {
1850
    return blogapi_error($user);
1860
  }
187
1881
  if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) {
189
    // Return an error if not configured type.
1900
    return $error;
1910
  }
192
1931
  $edit = array();
1941
  $edit['type'] = $blogid;
195
  // Get the node type defaults.
1961
  $node_type_default = variable_get('node_options_' . $edit['type'],
array('status', 'promote'));
1971
  $edit['uid'] = $user->uid;
1981
  $edit['name'] = $user->name;
1991
  $edit['promote'] = in_array('promote', $node_type_default);
2001
  $edit['comment'] = variable_get('comment_' . $edit['type'], 2);
2011
  $edit['revision'] = in_array('revision', $node_type_default);
2021
  $edit['format'] = FILTER_FORMAT_DEFAULT;
2031
  $edit['status'] = $publish;
204
205
  // Check for bloggerAPI vs. metaWeblogAPI.
2061
  if (is_array($content)) {
2070
    $edit['title'] = $content['title'];
2080
    $edit['body'] = $content['description'];
2090
    _blogapi_mt_extra($edit, $content);
2100
  }
211
  else {
2121
    $edit['title'] = blogapi_blogger_title($content);
2131
    $edit['body'] = $content;
214
  }
215
2161
  if (!node_access('create', $edit['type'])) {
2170
    return blogapi_error(t('You do not have permission to create this type
of post.'));
2180
  }
219
2201
  if (user_access('administer nodes') && !isset($edit['date'])) {
2210
    $edit['date'] = format_date(time(), 'custom', 'Y-m-d H:i:s O');
2220
  }
223
2241
  node_invoke_nodeapi($edit, 'blogapi new');
225
2261
  node_validate($edit);
2271
  if ($errors = form_get_errors()) {
2280
    return blogapi_error(implode("\n", $errors));
2290
  }
230
2311
  $node = node_submit($edit);
2321
  node_save($node);
2331
  if ($node->nid) {
2341
    watchdog('content', '@type: added %title using blog API.',
array('@type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE,
l(t('view'), "node/$node->nid"));
235
    // blogger.newPost returns a string so we cast the nid to a string by
putting it in double quotes.
2361
    return "$node->nid";
2370
  }
238
2390
  return blogapi_error(t('Error storing post.'));
2400
}
241
242
/**
243
 * Blogging API callback. Modifies the specified blog node.
244
 */
24525
function blogapi_blogger_edit_post($appkey, $postid, $username, $password,
$content, $publish) {
2461
  $user = blogapi_validate_user($username, $password);
247
2481
  if (!$user->uid) {
2490
    return blogapi_error($user);
2500
  }
251
2521
  $node = node_load($postid);
2531
  if (!$node) {
2540
    return blogapi_error(t('n/a'));
2550
  }
256
  // Let the teaser be re-generated.
2571
  unset($node->teaser);
258
2591
  if (!node_access('update', $node)) {
2600
    return blogapi_error(t('You do not have permission to update this
post.'));
2610
  }
262
2631
  $node->status = $publish;
264
265
  // check for bloggerAPI vs. metaWeblogAPI
2661
  if (is_array($content)) {
2670
    $node->title = $content['title'];
2680
    $node->body = $content['description'];
2690
    _blogapi_mt_extra($node, $content);
2700
  }
271
  else {
2721
    $node->title = blogapi_blogger_title($content);
2731
    $node->body = $content;
274
  }
275
2761
  node_invoke_nodeapi($node, 'blogapi edit');
277
2781
  node_validate($node);
2791
  if ($errors = form_get_errors()) {
2800
    return blogapi_error(implode("\n", $errors));
2810
  }
282
2831
  if (user_access('administer nodes') && !isset($edit['date'])) {
2840
    $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O');
2850
  }
2861
  $node = node_submit($node);
2871
  node_save($node);
2881
  if ($node->nid) {
2891
    watchdog('content', '@type: updated %title using Blog API.',
array('@type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE,
l(t('view'), "node/$node->nid"));
2901
    return TRUE;
2910
  }
292
2930
  return blogapi_error(t('Error storing post.'));
2940
}
295
296
/**
297
 * Blogging API callback. Returns a specified blog node.
298
 */
29925
function blogapi_blogger_get_post($appkey, $postid, $username, $password)
{
3000
  $user = blogapi_validate_user($username, $password);
3010
  if (!$user->uid) {
3020
    return blogapi_error($user);
3030
  }
304
3050
  $node = node_load($postid);
306
3070
  return _blogapi_get_post($node, TRUE);
3080
}
309
310
/**
311
 * Blogging API callback. Removes the specified blog node.
312
 */
31325
function blogapi_blogger_delete_post($appkey, $postid, $username,
$password, $publish) {
3141
  $user = blogapi_validate_user($username, $password);
3151
  if (!$user->uid) {
3160
    return blogapi_error($user);
3170
  }
318
3191
  node_delete($postid);
3201
  return TRUE;
3210
}
322
323
/**
324
 * Blogging API callback. Returns the latest few postings in a user's blog.
$bodies TRUE
325
 * <a
href="http://movabletype.org/docs/mtmanual_programmatic.html#item_mt%2EgetRecentPostTitles">
326
 * returns a bandwidth-friendly list</a>.
327
 */
32825
function blogapi_blogger_get_recent_posts($appkey, $blogid, $username,
$password, $number_of_posts, $bodies = TRUE) {
329
  // Remove unused appkey (from bloggerAPI).
3301
  $user = blogapi_validate_user($username, $password);
3311
  if (!$user->uid) {
3320
    return blogapi_error($user);
3330
  }
334
3351
  if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) {
336
    // Return an error if not configured type.
3370
    return $error;
3380
  }
339
3401
  if ($bodies) {
3411
    $result = db_query_range("SELECT n.nid, n.title, r.body, r.format,
n.comment, n.created, u.name FROM {node} n, {node_revisions} r, {users} u
WHERE n.uid = u.uid AND n.vid = r.vid AND n.type = '%s' AND n.uid = %d
ORDER BY n.created DESC",  $blogid, $user->uid, 0, $number_of_posts);
3421
  }
343
  else {
3440
    $result = db_query_range("SELECT n.nid, n.title, n.created, u.name FROM
{node} n, {users} u WHERE n.uid = u.uid AND n.type = '%s' AND n.uid = %d
ORDER BY n.created DESC", $blogid, $user->uid, 0, $number_of_posts);
345
  }
3461
  $blogs = array();
3471
  while ($blog = db_fetch_object($result)) {
3481
    $blogs[] = _blogapi_get_post($blog, $bodies);
3491
  }
350
3511
  return $blogs;
3520
}
353
35425
function blogapi_metaweblog_new_post($blogid, $username, $password,
$content, $publish) {
3550
  return blogapi_blogger_new_post('0123456789ABCDEF', $blogid, $username,
$password, $content, $publish);
3560
}
357
35825
function blogapi_metaweblog_edit_post($postid, $username, $password,
$content, $publish) {
3590
  return blogapi_blogger_edit_post('0123456789ABCDEF', $postid, $username,
$password, $content, $publish);
3600
}
361
36225
function blogapi_metaweblog_get_post($postid, $username, $password) {
3630
  return blogapi_blogger_get_post('01234567890ABCDEF', $postid, $username,
$password);
3640
}
365
366
/**
367
 * Blogging API callback. Inserts a file into Drupal.
368
 */
36925
function blogapi_metaweblog_new_media_object($blogid, $username, $password,
$file) {
3701
  $user = blogapi_validate_user($username, $password);
3711
  if (!$user->uid) {
3720
    return blogapi_error($user);
3730
  }
374
3751
  $name = basename($file['name']);
3761
  $data = $file['bits'];
377
3781
  if (!$data) {
3790
    return blogapi_error(t('No file sent.'));
3800
  }
381
3821
  if (!$file = file_save_data($data, $name)) {
3830
    return blogapi_error(t('Error storing file.'));
3840
  }
385
386
  // Return the successful result.
3871
  return array('url' => file_create_url($file), 'struct');
3880
}
389
/**
390
 * Blogging API callback. Returns a list of the taxonomy terms that can be
391
 * associated with a blog node.
392
 */
39325
function blogapi_metaweblog_get_category_list($blogid, $username,
$password) {
3940
  if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) {
395
    // Return an error if not configured type.
3960
    return $error;
3970
  }
398
3990
  $vocabularies = module_invoke('taxonomy', 'get_vocabularies', $blogid,
'vid');
4000
  $categories = array();
4010
  if ($vocabularies) {
4020
    foreach ($vocabularies as $vocabulary) {
4030
      $terms = module_invoke('taxonomy', 'get_tree', $vocabulary->vid, 0,
-1);
4040
      foreach ($terms as $term) {
4050
        $term_name = $term->name;
4060
        foreach (module_invoke('taxonomy', 'get_parents', $term->tid,
'tid') as $parent) {
4070
          $term_name = $parent->name . '/' . $term_name;
4080
        }
4090
        $categories[] = array('categoryName' => $term_name, 'categoryId' =>
$term->tid);
4100
      }
4110
    }
4120
  }
413
4140
  return $categories;
4150
}
416
41725
function blogapi_metaweblog_get_recent_posts($blogid, $username, $password,
$number_of_posts) {
4180
  return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid,
$username, $password, $number_of_posts, TRUE);
4190
}
420
42125
function blogapi_mt_get_recent_post_titles($blogid, $username, $password,
$number_of_posts) {
4220
  return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid,
$username, $password, $number_of_posts, FALSE);
4230
}
424
42525
function blogapi_mt_get_category_list($blogid, $username, $password) {
4260
  return blogapi_metaweblog_get_category_list($blogid, $username,
$password);
4270
}
428
429
/**
430
 * Blogging API callback. Returns a list of the taxonomy terms that are
431
 * assigned to a particular node.
432
 */
43325
function blogapi_mt_get_post_categories($postid, $username, $password) {
4341
  $user = blogapi_validate_user($username, $password);
4351
  if (!$user->uid) {
4360
    return blogapi_error($user);
4370
  }
438
4391
  $node = node_load($postid);
4401
  $terms = module_invoke('taxonomy', 'node_get_terms', $node, 'tid');
4411
  $categories = array();
4421
  foreach ($terms as $term) {
4431
    $term_name = $term->name;
4441
    foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as
$parent) {
4450
      $term_name = $parent->name . '/' . $term_name;
4460
    }
4471
    $categories[] = array('categoryName' => $term_name, 'categoryId' =>
$term->tid, 'isPrimary' => TRUE);
4481
  }
449
4501
  return $categories;
4510
}
452
453
/**
454
 * Blogging API callback. Assigns taxonomy terms to a particular node.
455
 */
45625
function blogapi_mt_set_post_categories($postid, $username, $password,
$categories) {
4571
  $user = blogapi_validate_user($username, $password);
4581
  if (!$user->uid) {
4590
    return blogapi_error($user);
4600
  }
461
4621
  $node = node_load($postid);
4631
  $node->taxonomy = array();
4641
  foreach ($categories as $category) {
4651
    $node->taxonomy[] = $category['categoryId'];
4661
  }
4671
  node_save($node);
468
4691
  return TRUE;
4700
}
471
472
/**
473
 * Blogging API callback. Sends a list of available input formats.
474
 */
47525
function blogapi_mt_supported_text_filters() {
476
  // NOTE: we're only using anonymous' formats because the MT spec
477
  // does not allow for per-user formats.
4780
  $formats = filter_formats();
479
4800
  $filters = array();
4810
  foreach ($formats as $format) {
4820
    $filter['key'] = $format->format;
4830
    $filter['label'] = $format->name;
4840
    $filters[] = $filter;
4850
  }
486
4870
  return $filters;
4880
}
489
490
/**
491
 * Blogging API callback. Publishes the given node.
492
 */
49325
function blogapi_mt_publish_post($postid, $username, $password) {
4940
  $user = blogapi_validate_user($username, $password);
495
4960
  if (!$user->uid) {
4970
    return blogapi_error($user);
4980
  }
4990
  $node = node_load($postid);
500
5010
  if (!$node) {
5020
    return blogapi_error(t('Invalid post.'));
5030
  }
504
5050
  $node->status = 1;
5060
  if (!node_access('update', $node)) {
5070
    return blogapi_error(t('You do not have permission to update this
post.'));
5080
  }
509
5100
  node_save($node);
511
5120
  return TRUE;
5130
}
514
515
/**
516
 * Prepare an error message for returning to the XMLRPC caller.
517
 */
51825
function blogapi_error($message) {
5190
  static $xmlrpcusererr;
520
5210
  if (!is_array($message)) {
5220
    $message = array($message);
5230
  }
524
5250
  $message = implode(' ', $message);
526
5270
  return xmlrpc_error($xmlrpcusererr + 1, strip_tags($message));
5280
}
529
530
/**
531
 * Ensure that the given user has permission to edit a blog.
532
 */
53325
function blogapi_validate_user($username, $password) {
5348
  global $user;
535
5368
  $user = user_authenticate(array('name' => $username, 'pass' =>
$password));
537
5388
  if ($user->uid) {
5398
    if (user_access('administer content with blog api', $user)) {
5408
      return $user;
5410
    }
542
    else {
5430
      return t('You do not have permission to edit this blog.');
544
    }
5450
  }
546
  else {
5470
    return t('Wrong username or password.');
548
  }
5490
}
550
551
/**
552
 * For the blogger API, extract the node title from the contents field.
553
 */
55425
function blogapi_blogger_title(&$contents) {
5552
  if (eregi('<title>([^<]*)</title>', $contents, $title)) {
5560
    $title = strip_tags($title[0]);
5570
    $contents = ereg_replace('<title>[^<]*</title>', '', $contents);
5580
  }
559
  else {
5602
    list($title, $contents) = explode("\n", $contents, 2);
561
  }
562
5632
  return $title;
5640
}
565
566
/**
567
 * Add some settings to the admin_settings form.
568
 */
56925
function blogapi_admin_settings() {
5700
  $node_types = array_map('check_plain', node_get_types('names'));
5710
  $defaults = isset($node_types['blog']) ? array('blog' => 1) : array();
5720
  $form['blogapi_node_types'] = array(
5730
    '#type' => 'checkboxes',
5740
    '#title' => t('Enable for external blogging clients'),
5750
    '#required' => TRUE,
5760
    '#default_value' => variable_get('blogapi_node_types', $defaults),
5770
    '#options' => $node_types,
5780
    '#description' => t('Select the content types available to external
blogging clients via Blog API. If supported, each enabled content type will
be displayed as a separate "blog" by the external client.')
5790
  );
580
5810
  return system_settings_form($form);
5820
}
583
584
/**
585
 * Implementation of hook_menu().
586
 */
58725
function blogapi_menu() {
5881
  $items['blogapi/rsd'] = array(
5891
    'title' => 'RSD',
5901
    'page callback' => 'blogapi_rsd',
5911
    'access arguments' => array('access content'),
5921
    'type' => MENU_CALLBACK,
593
  );
5941
  $items['admin/settings/blogapi'] = array(
5951
    'title' => 'Blog API',
5961
    'description' => 'Configure the content types available to external
blogging clients.',
5971
    'page callback' => 'drupal_get_form',
5981
    'page arguments' => array('blogapi_admin_settings'),
5991
    'access arguments' => array('administer site configuration'),
6001
    'type' => MENU_NORMAL_ITEM,
601
  );
602
6031
  return $items;
6040
}
605
606
/**
607
 * Implementaton of hook_init().
608
 */
60925
function blogapi_init() {
61024
  if (drupal_is_front_page()) {
61110
    drupal_add_link(array('rel' => 'EditURI',
61210
                          'type' => 'application/rsd+xml',
61310
                          'title' => t('RSD'),
61410
                          'href' => url('blogapi/rsd', array('absolute' =>
TRUE))));
61510
  }
61624
}
617
61825
function blogapi_rsd() {
6190
  global $base_url;
620
6210
  $xmlrpc = $base_url . '/xmlrpc.php';
6220
  $base = url('', array('absolute' => TRUE));
6230
  $blogid = 1; # until we figure out how to handle multiple bloggers
624
6250
  drupal_set_header('Content-Type: application/rsd+xml; charset=utf-8');
626
  print <<<__RSD__
6270
<?xml version="1.0"?>
628
<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
629
  <service>
630
    <engineName>Drupal</engineName>
631
    <engineLink>http://drupal.org/</engineLink>
6320
    <homePageLink>$base</homePageLink>
633
    <apis>
6340
      <api name="MetaWeblog" preferred="false" apiLink="$xmlrpc"
blogID="$blogid" />
6350
      <api name="Blogger" preferred="false" apiLink="$xmlrpc"
blogID="$blogid" />
6360
      <api name="MovableType" preferred="true" apiLink="$xmlrpc"
blogID="$blogid" />
637
    </apis>
638
  </service>
6390
</rsd>
6400
__RSD__;
6410
}
642
643
/**
644
 * Handles extra information sent by clients according to MovableType's
spec.
645
 */
64625
function _blogapi_mt_extra(&$node, $struct) {
6470
  if (is_array($node)) {
6480
    $was_array = TRUE;
6490
    $node = (object)$node;
6500
  }
651
6520
  if (array_key_exists('mt_allow_comments', $struct)) {
6530
    switch ($struct['mt_allow_comments']) {
6540
      case 0:
6550
        $node->comment = COMMENT_NODE_DISABLED;
6560
        break;
6570
      case 1:
6580
        $node->comment = COMMENT_NODE_READ_WRITE;
6590
        break;
6600
      case 2:
6610
        $node->comment = COMMENT_NODE_READ_ONLY;
6620
        break;
6630
    }
6640
  }
665
666
  // Merge the 3 body sections (description, mt_excerpt, mt_text_more) into
one body.
6670
  if ($struct['mt_excerpt']) {
6680
    $node->body = $struct['mt_excerpt'] . '<!--break-->' . $node->body;
6690
  }
6700
  if ($struct['mt_text_more']) {
6710
    $node->body = $node->body . '<!--extended-->' .
$struct['mt_text_more'];
6720
  }
673
6740
  if ($struct['mt_convert_breaks']) {
6750
    $node->format = $struct['mt_convert_breaks'];
6760
  }
677
6780
  if ($struct['dateCreated']) {
6790
    $node->date = format_date(mktime($struct['dateCreated']->hour,
$struct['dateCreated']->minute, $struct['dateCreated']->second,
$struct['dateCreated']->month, $struct['dateCreated']->day,
$struct['dateCreated']->year), 'custom', 'Y-m-d H:i:s O');
6800
  }
681
6820
  if ($was_array) {
6830
    $node = (array)$node;
6840
  }
6850
}
686
68725
function _blogapi_get_post($node, $bodies = TRUE) {
688
  $xmlrpcval = array(
6891
    'userid' => $node->name,
6901
    'dateCreated' => xmlrpc_date($node->created),
6911
    'title' => $node->title,
6921
    'postid' => $node->nid,
6931
    'link' => url('node/' . $node->nid, array('absolute' => TRUE)),
6941
    'permaLink' => url('node/' . $node->nid, array('absolute' => TRUE)),
6951
  );
696
6971
  if ($bodies) {
6981
    if ($node->comment == 1) {
6990
      $comment = 2;
7000
    }
7011
    elseif ($node->comment == 2) {
7021
      $comment = 1;
7031
    }
7041
    $xmlrpcval['content'] = "<title>$node->title</title>$node->body";
7051
    $xmlrpcval['description'] = $node->body;
706
    // Add MT specific fields
7071
    $xmlrpcval['mt_allow_comments'] = (int) $comment;
7081
    $xmlrpcval['mt_convert_breaks'] = $node->format;
7091
  }
710
7111
  return $xmlrpcval;
7120
}
713
714
/**
715
 * Validate blog ID, which maps to a content type in Drupal.
716
 *
717
 * Only content types configured to work with Blog API are supported.
718
 *
719
 * @return
720
 *   TRUE if the content type is supported and the user has permission
721
 *   to post, or a blogapi_error() XML construct otherwise.
722
 */
72325
function _blogapi_validate_blogid($blogid) {
7242
  $types = _blogapi_get_node_types();
7252
  if (in_array($blogid, $types, TRUE)) {
7262
    return TRUE;
7270
  }
728
7290
  return blogapi_error(t("Blog API module is not configured to support the
%type content type, or you don't have sufficient permissions to post this
type of content.", array('%type' => $blogid)));
7300
}
731
73225
function _blogapi_get_node_types() {
7333
  $available_types =
array_keys(array_filter(variable_get('blogapi_node_types', array('blog' =>
1))));
7343
  $types = array();
7353
  foreach (node_get_types() as $type => $name) {
7363
    if (node_access('create', $type) && in_array($type, $available_types))
{
7373
      $types[] = $type;
7383
    }
7393
  }
740
7413
  return $types;
7420
}
74325