[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/modules/blogapi/ -> blogapi.module (source)

   1  <?php
   2  // $Id: blogapi.module,v 1.115.2.5 2008/10/08 20:12:17 goba 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   */
  12  function blogapi_help($path, $arg) {
  13    switch ($path) {
  14      case 'admin/help#blogapi':
  15        $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>';
  16        $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>';
  17        $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>';
  18        $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>';
  19        return $output;
  20    }
  21  }
  22  
  23  /**
  24   * Implementation of hook_perm().
  25   */
  26  function blogapi_perm() {
  27    return array('administer content with blog api');
  28  }
  29  
  30  /**
  31   * Implementation of hook_xmlrpc().
  32   */
  33  function blogapi_xmlrpc() {
  34    return array(
  35      array(
  36        'blogger.getUsersBlogs',
  37        'blogapi_blogger_get_users_blogs',
  38        array('array', 'string', 'string', 'string'),
  39        t('Returns a list of blogs to which an author has posting privileges.')),
  40      array(
  41        'blogger.getUserInfo',
  42        'blogapi_blogger_get_user_info',
  43        array('struct', 'string', 'string', 'string'),
  44        t('Returns information about an author in the system.')),
  45      array(
  46        'blogger.newPost',
  47        'blogapi_blogger_new_post',
  48        array('string', 'string', 'string', 'string', 'string', 'string', 'boolean'),
  49        t('Creates a new post, and optionally publishes it.')),
  50      array(
  51        'blogger.editPost',
  52        'blogapi_blogger_edit_post',
  53        array('boolean', 'string', 'string', 'string', 'string', 'string', 'boolean'),
  54        t('Updates the information about an existing post.')),
  55      array(
  56        'blogger.getPost',
  57        'blogapi_blogger_get_post',
  58        array('struct', 'string', 'string', 'string', 'string'),
  59        t('Returns information about a specific post.')),
  60      array(
  61        'blogger.deletePost',
  62        'blogapi_blogger_delete_post',
  63        array('boolean', 'string', 'string', 'string', 'string', 'boolean'),
  64        t('Deletes a post.')),
  65      array(
  66        'blogger.getRecentPosts',
  67        'blogapi_blogger_get_recent_posts',
  68        array('array', 'string', 'string', 'string', 'string', 'int'),
  69        t('Returns a list of the most recent posts in the system.')),
  70      array(
  71        'metaWeblog.newPost',
  72        'blogapi_metaweblog_new_post',
  73        array('string', 'string', 'string', 'string', 'struct', 'boolean'),
  74        t('Creates a new post, and optionally publishes it.')),
  75      array(
  76        'metaWeblog.editPost',
  77        'blogapi_metaweblog_edit_post',
  78        array('boolean', 'string', 'string', 'string', 'struct', 'boolean'),
  79        t('Updates information about an existing post.')),
  80      array(
  81        'metaWeblog.getPost',
  82        'blogapi_metaweblog_get_post',
  83        array('struct', 'string', 'string', 'string'),
  84        t('Returns information about a specific post.')),
  85      array(
  86        'metaWeblog.newMediaObject',
  87        'blogapi_metaweblog_new_media_object',
  88        array('string', 'string', 'string', 'string', 'struct'),
  89        t('Uploads a file to your webserver.')),
  90      array(
  91        'metaWeblog.getCategories',
  92        'blogapi_metaweblog_get_category_list',
  93        array('struct', 'string', 'string', 'string'),
  94        t('Returns a list of all categories to which the post is assigned.')),
  95      array(
  96        'metaWeblog.getRecentPosts',
  97        'blogapi_metaweblog_get_recent_posts',
  98        array('array', 'string', 'string', 'string', 'int'),
  99        t('Returns a list of the most recent posts in the system.')),
 100      array(
 101        'mt.getRecentPostTitles',
 102        'blogapi_mt_get_recent_post_titles',
 103        array('array', 'string', 'string', 'string', 'int'),
 104        t('Returns a bandwidth-friendly list of the most recent posts in the system.')),
 105      array(
 106        'mt.getCategoryList',
 107        'blogapi_mt_get_category_list',
 108        array('array', 'string', 'string', 'string'),
 109        t('Returns a list of all categories defined in the blog.')),
 110      array(
 111        'mt.getPostCategories',
 112        'blogapi_mt_get_post_categories',
 113        array('array', 'string', 'string', 'string'),
 114        t('Returns a list of all categories to which the post is assigned.')),
 115      array(
 116        'mt.setPostCategories',
 117        'blogapi_mt_set_post_categories',
 118        array('boolean', 'string', 'string', 'string', 'array'),
 119        t('Sets the categories for a post.')),
 120      array(
 121        'mt.supportedMethods',
 122        'xmlrpc_server_list_methods',
 123        array('array'),
 124        t('Retrieve information about the XML-RPC methods supported by the server.')),
 125      array(
 126        'mt.supportedTextFilters',
 127        'blogapi_mt_supported_text_filters',
 128        array('array'),
 129        t('Retrieve information about the text formatting plugins supported by the server.')),
 130      array(
 131        'mt.publishPost',
 132        'blogapi_mt_publish_post',
 133        array('boolean', 'string', 'string', 'string'),
 134        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).')));
 135  }
 136  
 137  /**
 138   * Blogging API callback. Finds the URL of a user's blog.
 139   */
 140  
 141  function blogapi_blogger_get_users_blogs($appid, $username, $password) {
 142  
 143    $user = blogapi_validate_user($username, $password);
 144    if ($user->uid) {
 145      $types = _blogapi_get_node_types();
 146      $structs = array();
 147      foreach ($types as $type) {
 148        $structs[] = array('url' => url('blog/'. $user->uid, array('absolute' => TRUE)), 'blogid' => $type, 'blogName' => $user->name .": ". $type);
 149      }
 150      return $structs;
 151    }
 152    else {
 153      return blogapi_error($user);
 154    }
 155  }
 156  
 157  /**
 158   * Blogging API callback. Returns profile information about a user.
 159   */
 160  function blogapi_blogger_get_user_info($appkey, $username, $password) {
 161    $user = blogapi_validate_user($username, $password);
 162  
 163    if ($user->uid) {
 164      $name = explode(' ', $user->realname ? $user->realname : $user->name, 2);
 165      return array(
 166        'userid' => $user->uid,
 167        'lastname' => $name[1],
 168        'firstname' => $name[0],
 169        'nickname' => $user->name,
 170        'email' => $user->mail,
 171        'url' => url('blog/'. $user->uid, array('absolute' => TRUE)));
 172    }
 173    else {
 174      return blogapi_error($user);
 175    }
 176  }
 177  
 178  /**
 179   * Blogging API callback. Inserts a new blog post as a node.
 180   */
 181  function blogapi_blogger_new_post($appkey, $blogid, $username, $password, $content, $publish) {
 182    $user = blogapi_validate_user($username, $password);
 183    if (!$user->uid) {
 184      return blogapi_error($user);
 185    }
 186  
 187    if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) {
 188      // Return an error if not configured type.
 189      return $error;
 190    }
 191  
 192    $edit = array();
 193    $edit['type'] = $blogid;
 194    // get the node type defaults
 195    $node_type_default = variable_get('node_options_'. $edit['type'], array('status', 'promote'));
 196    $edit['uid'] = $user->uid;
 197    $edit['name'] = $user->name;
 198    $edit['promote'] = in_array('promote', $node_type_default);
 199    $edit['comment'] = variable_get('comment_'. $edit['type'], 2);
 200    $edit['revision'] = in_array('revision', $node_type_default);
 201    $edit['format'] = FILTER_FORMAT_DEFAULT;
 202    $edit['status'] = $publish;
 203  
 204    // check for bloggerAPI vs. metaWeblogAPI
 205    if (is_array($content)) {
 206      $edit['title'] = $content['title'];
 207      $edit['body'] = $content['description'];
 208      _blogapi_mt_extra($edit, $content);
 209    }
 210    else {
 211      $edit['title'] = blogapi_blogger_title($content);
 212      $edit['body'] = $content;
 213    }
 214  
 215    if (!node_access('create', $edit['type'])) {
 216      return blogapi_error(t('You do not have permission to create this type of post.'));
 217    }
 218  
 219    if (user_access('administer nodes') && !isset($edit['date'])) {
 220      $edit['date'] = format_date(time(), 'custom', 'Y-m-d H:i:s O');
 221    }
 222  
 223    node_invoke_nodeapi($edit, 'blogapi new');
 224  
 225    $valid = blogapi_status_error_check($edit, $publish);
 226    if ($valid !== TRUE) {
 227      return $valid;
 228    }
 229  
 230    node_validate($edit);
 231    if ($errors = form_get_errors()) {
 232      return blogapi_error(implode("\n", $errors));
 233    }
 234  
 235    $node = node_submit($edit);
 236    node_save($node);
 237    if ($node->nid) {
 238      watchdog('content', '@type: added %title using blog API.', array('@type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid"));
 239      // blogger.newPost returns a string so we cast the nid to a string by putting it in double quotes:
 240      return "$node->nid";
 241    }
 242  
 243    return blogapi_error(t('Error storing post.'));
 244  }
 245  
 246  /**
 247   * Blogging API callback. Modifies the specified blog node.
 248   */
 249  function blogapi_blogger_edit_post($appkey, $postid, $username, $password, $content, $publish) {
 250  
 251    $user = blogapi_validate_user($username, $password);
 252  
 253    if (!$user->uid) {
 254      return blogapi_error($user);
 255    }
 256  
 257    $node = node_load($postid);
 258    if (!$node) {
 259      return blogapi_error(t('n/a'));
 260    }
 261    // Let the teaser be re-generated.
 262    unset($node->teaser);
 263  
 264    if (!node_access('update', $node)) {
 265      return blogapi_error(t('You do not have permission to update this post.'));
 266    }
 267    // Save the original status for validation of permissions.
 268    $original_status = $node->status;
 269    $node->status = $publish;
 270  
 271    // check for bloggerAPI vs. metaWeblogAPI
 272    if (is_array($content)) {
 273      $node->title = $content['title'];
 274      $node->body = $content['description'];
 275      _blogapi_mt_extra($node, $content);
 276    }
 277    else {
 278      $node->title = blogapi_blogger_title($content);
 279      $node->body = $content;
 280    }
 281  
 282    node_invoke_nodeapi($node, 'blogapi edit');
 283  
 284    $valid = blogapi_status_error_check($node, $original_status);
 285    if ($valid !== TRUE) {
 286      return $valid;
 287    }
 288  
 289    node_validate($node);
 290    if ($errors = form_get_errors()) {
 291      return blogapi_error(implode("\n", $errors));
 292    }
 293  
 294    if (user_access('administer nodes') && !isset($edit['date'])) {
 295      $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O');
 296    }
 297    $node = node_submit($node);
 298    node_save($node);
 299    if ($node->nid) {
 300      watchdog('content', '@type: updated %title using Blog API.', array('@type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid"));
 301      return TRUE;
 302    }
 303  
 304    return blogapi_error(t('Error storing post.'));
 305  }
 306  
 307  /**
 308   * Blogging API callback. Returns a specified blog node.
 309   */
 310  function blogapi_blogger_get_post($appkey, $postid, $username, $password) {
 311    $user = blogapi_validate_user($username, $password);
 312    if (!$user->uid) {
 313      return blogapi_error($user);
 314    }
 315  
 316    $node = node_load($postid);
 317  
 318    return _blogapi_get_post($node, TRUE);
 319  }
 320  
 321  /**
 322   * Check that the user has permission to save the node with the chosen status.
 323   *
 324   * @return
 325   *   TRUE if no error, or the blogapi_error().
 326   */
 327  function blogapi_status_error_check($node, $original_status) {
 328    
 329    $node = (object) $node;
 330  
 331    $node_type_default = variable_get('node_options_'. $node->type, array('status', 'promote'));
 332  
 333    // If we don't have the 'administer nodes' permission and the status is
 334    // changing or for a new node the status is not the content type's default,
 335    // then return an error.
 336    if (!user_access('administer nodes') && (($node->status != $original_status) || (empty($node->nid) && $node->status != in_array('status', $node_type_default)))) {
 337      if ($node->status) {
 338        return blogapi_error(t('You do not have permission to publish this type of post. Please save it as a draft instead.'));
 339      }
 340      else {
 341        return blogapi_error(t('You do not have permission to save this post as a draft. Please publish it instead.'));
 342      }
 343    }
 344    return TRUE;
 345  }
 346  
 347  
 348  /**
 349   * Blogging API callback. Removes the specified blog node.
 350   */
 351  function blogapi_blogger_delete_post($appkey, $postid, $username, $password, $publish) {
 352    $user = blogapi_validate_user($username, $password);
 353    if (!$user->uid) {
 354      return blogapi_error($user);
 355    }
 356  
 357    node_delete($postid);
 358    return TRUE;
 359  }
 360  
 361  /**
 362   * Blogging API callback. Returns the latest few postings in a user's blog. $bodies TRUE
 363   * <a href="http://movabletype.org/docs/mtmanual_programmatic.html#item_mt%2EgetRecentPostTitles">
 364   * returns a bandwidth-friendly list</a>.
 365   */
 366  function blogapi_blogger_get_recent_posts($appkey, $blogid, $username, $password, $number_of_posts, $bodies = TRUE) {
 367    // Remove unused appkey (from bloggerAPI).
 368    $user = blogapi_validate_user($username, $password);
 369    if (!$user->uid) {
 370      return blogapi_error($user);
 371    }
 372  
 373    if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) {
 374      // Return an error if not configured type.
 375      return $error;
 376    }
 377  
 378    if ($bodies) {
 379      $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);
 380    }
 381    else {
 382      $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);
 383    }
 384    $blogs = array();
 385    while ($blog = db_fetch_object($result)) {
 386      $blogs[] = _blogapi_get_post($blog, $bodies);
 387    }
 388    return $blogs;
 389  }
 390  
 391  function blogapi_metaweblog_new_post($blogid, $username, $password, $content, $publish) {
 392    return blogapi_blogger_new_post('0123456789ABCDEF', $blogid, $username, $password, $content, $publish);
 393  }
 394  
 395  function blogapi_metaweblog_edit_post($postid, $username, $password, $content, $publish) {
 396    return blogapi_blogger_edit_post('0123456789ABCDEF', $postid, $username, $password, $content, $publish);
 397  }
 398  
 399  function blogapi_metaweblog_get_post($postid, $username, $password) {
 400    return blogapi_blogger_get_post('01234567890ABCDEF', $postid, $username, $password);
 401  }
 402  
 403  /**
 404   * Blogging API callback. Inserts a file into Drupal.
 405   */
 406  function blogapi_metaweblog_new_media_object($blogid, $username, $password, $file) {
 407    $user = blogapi_validate_user($username, $password);
 408    if (!$user->uid) {
 409      return blogapi_error($user);
 410    }
 411  
 412    $usersize = 0;
 413    $uploadsize = 0;
 414  
 415    $roles = array_intersect(user_roles(FALSE, 'administer content with blog api'), $user->roles);
 416  
 417    foreach ($roles as $rid => $name) {
 418      $extensions .= ' '. strtolower(variable_get("blogapi_extensions_$rid", variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp')));
 419      $usersize= max($usersize, variable_get("blogapi_usersize_$rid", variable_get('blogapi_usersize_default', 1)) * 1024 * 1024);
 420      $uploadsize = max($uploadsize, variable_get("blogapi_uploadsize_$rid", variable_get('blogapi_uploadsize_default', 1)) * 1024 * 1024);
 421    }
 422  
 423    $filesize = strlen($file['bits']);
 424  
 425    if ($filesize > $uploadsize) {
 426      return blogapi_error(t('It is not possible to upload the file, because it exceeded the maximum filesize of @maxsize.', array('@maxsize' => format_size($uploadsize))));
 427    }
 428  
 429    if (_blogapi_space_used($user->uid) + $filesize > $usersize) {
 430      return blogapi_error(t('The file can not be attached to this post, because the disk quota of @quota has been reached.', array('@quota' => format_size($usersize))));
 431    }
 432  
 433    // Only allow files with whitelisted extensions and convert remaining dots to
 434    // underscores to prevent attacks via non-terminal executable extensions with
 435    // files such as exploit.php.jpg.
 436  
 437    $whitelist = array_unique(explode(' ', trim($extensions)));
 438  
 439    $name = basename($file['name']);
 440  
 441    if ($extension_position = strrpos($name, '.')) {
 442      $filename = drupal_substr($name, 0, $extension_position);
 443      $final_extension = drupal_substr($name, $extension_position + 1);
 444  
 445      if (!in_array(strtolower($final_extension), $whitelist)) {
 446        return blogapi_error(t('It is not possible to upload the file, because it is only possible to upload files with the following extensions: @extensions', array('@extensions' => implode(' ', $whitelist))));
 447      }
 448  
 449      $filename = str_replace('.', '_', $filename);
 450      $filename .= '.'. $final_extension;
 451    }
 452  
 453    $data = $file['bits'];
 454  
 455    if (!$data) {
 456      return blogapi_error(t('No file sent.'));
 457    }
 458  
 459    if (!$file = file_save_data($data, $filename)) {
 460      return blogapi_error(t('Error storing file.'));
 461    }
 462  
 463    $row = new stdClass();
 464    $row->uid = $user->uid;
 465    $row->filepath = $file;
 466    $row->filesize = $filesize;
 467  
 468    drupal_write_record('blogapi_files', $row);
 469  
 470    // Return the successful result.
 471    return array('url' => file_create_url($file), 'struct');
 472  }
 473  /**
 474   * Blogging API callback. Returns a list of the taxonomy terms that can be
 475   * associated with a blog node.
 476   */
 477  function blogapi_metaweblog_get_category_list($blogid, $username, $password) {
 478    $user = blogapi_validate_user($username, $password);
 479    if (!$user->uid) {
 480      return blogapi_error($user);
 481    }
 482  
 483    if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) {
 484      // Return an error if not configured type.
 485      return $error;
 486    }
 487  
 488    $vocabularies = module_invoke('taxonomy', 'get_vocabularies', $blogid, 'vid');
 489    $categories = array();
 490    if ($vocabularies) {
 491      foreach ($vocabularies as $vocabulary) {
 492        $terms = module_invoke('taxonomy', 'get_tree', $vocabulary->vid, 0, -1);
 493        foreach ($terms as $term) {
 494          $term_name = $term->name;
 495          foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) {
 496            $term_name = $parent->name .'/'. $term_name;
 497          }
 498          $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid);
 499        }
 500      }
 501    }
 502    return $categories;
 503  }
 504  
 505  function blogapi_metaweblog_get_recent_posts($blogid, $username, $password, $number_of_posts) {
 506    return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, TRUE);
 507  }
 508  
 509  function blogapi_mt_get_recent_post_titles($blogid, $username, $password, $number_of_posts) {
 510    return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, FALSE);
 511  }
 512  
 513  function blogapi_mt_get_category_list($blogid, $username, $password) {
 514    return blogapi_metaweblog_get_category_list($blogid, $username, $password);
 515  }
 516  
 517  /**
 518   * Blogging API callback. Returns a list of the taxonomy terms that are
 519   * assigned to a particular node.
 520   */
 521  function blogapi_mt_get_post_categories($postid, $username, $password) {
 522    $user = blogapi_validate_user($username, $password);
 523    if (!$user->uid) {
 524      return blogapi_error($user);
 525    }
 526  
 527    $node = node_load($postid);
 528    $terms = module_invoke('taxonomy', 'node_get_terms', $node, 'tid');
 529    $categories = array();
 530    foreach ($terms as $term) {
 531      $term_name = $term->name;
 532      foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) {
 533        $term_name = $parent->name .'/'. $term_name;
 534      }
 535      $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid, 'isPrimary' => TRUE);
 536    }
 537  
 538    return $categories;
 539  }
 540  
 541  /**
 542   * Blogging API callback. Assigns taxonomy terms to a particular node.
 543   */
 544  function blogapi_mt_set_post_categories($postid, $username, $password, $categories) {
 545    $user = blogapi_validate_user($username, $password);
 546    if (!$user->uid) {
 547      return blogapi_error($user);
 548    }
 549  
 550    $node = node_load($postid);
 551    $node->taxonomy = array();
 552    foreach ($categories as $category) {
 553      $node->taxonomy[] = $category['categoryId'];
 554    }
 555    $validated = blogapi_mt_validate_terms($node);
 556    if ($validated !== TRUE) {
 557      return $validated;
 558    }
 559    node_save($node);
 560    return TRUE;
 561  }
 562  
 563  /**
 564   * Blogging API helper - find allowed taxonomy terms for a node type.
 565   */
 566  function blogapi_mt_validate_terms($node) {
 567    // We do a lot of heavy lifting here since taxonomy module doesn't have a
 568    // stand-alone validation function.
 569    if (module_exists('taxonomy')) {
 570      $found_terms = array();
 571      if (!empty($node->taxonomy)) {
 572        $term_list = array_unique($node->taxonomy);
 573        $params = $term_list;
 574        $params[] = $node->type;
 575        $result = db_query(db_rewrite_sql("SELECT t.tid, t.vid FROM {term_data} t INNER JOIN {vocabulary_node_types} n ON t.vid = n.vid WHERE t.tid IN (". db_placeholders($term_list) .") AND n.type = '%s'", 't', 'tid'), $params);
 576        $found_terms = array();
 577        $found_count = 0;
 578        while ($term = db_fetch_object($result)) {
 579          $found_terms[$term->vid][$term->tid] = $term->tid;
 580          $found_count++;
 581        }
 582        // If the counts don't match, some terms are invalid or not accessible to this user.
 583        if (count($term_list) != $found_count) {
 584          return blogapi_error(t('Invalid categories submitted.'));
 585        }
 586      }
 587      // Look up all the vocabularies for this node type.
 588      $result2 = db_query(db_rewrite_sql("SELECT v.vid, v.name, v.required, v.multiple FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s'", 'v', 'vid'), $node->type);
 589      // Check each vocabulary associated with this node type.
 590      while ($vocabulary = db_fetch_object($result2)) {
 591        // Required vocabularies must have at least one term.
 592        if ($vocabulary->required && empty($found_terms[$vocabulary->vid])) {
 593          return blogapi_error(t('A category from the @vocabulary_name vocabulary is required.', array('@vocabulary_name' => $vocabulary->name)));
 594        }
 595        // Vocabularies that don't allow multiple terms may have at most one.
 596        if (!($vocabulary->multiple) && (isset($found_terms[$vocabulary->vid]) && count($found_terms[$vocabulary->vid]) > 1)) {
 597          return blogapi_error(t('You may only choose one category from the @vocabulary_name vocabulary.'), array('@vocabulary_name' => $vocabulary->name));
 598        }
 599      }
 600    }
 601    elseif (!empty($node->taxonomy)) {
 602      return blogapi_error(t('Error saving categories. This feature is not available.'));
 603    }
 604    return TRUE;
 605  }
 606  
 607  /**
 608   * Blogging API callback. Sends a list of available input formats.
 609   */
 610  function blogapi_mt_supported_text_filters() {
 611    // NOTE: we're only using anonymous' formats because the MT spec
 612    // does not allow for per-user formats.
 613    $formats = filter_formats();
 614  
 615    $filters = array();
 616    foreach ($formats as $format) {
 617      $filter['key'] = $format->format;
 618      $filter['label'] = $format->name;
 619      $filters[] = $filter;
 620    }
 621  
 622    return $filters;
 623  }
 624  
 625  /**
 626   * Blogging API callback. Publishes the given node
 627   */
 628  function blogapi_mt_publish_post($postid, $username, $password) {
 629    $user = blogapi_validate_user($username, $password);
 630    if (!$user->uid) {
 631      return blogapi_error($user);
 632    }
 633    $node = node_load($postid);
 634    if (!$node) {
 635      return blogapi_error(t('Invalid post.'));
 636    }
 637  
 638    // Nothing needs to be done if already published.
 639    if ($node->status) {
 640      return;
 641    }
 642  
 643    if (!node_access('update', $node) || !user_access('administer nodes')) {
 644      return blogapi_error(t('You do not have permission to update this post.'));
 645    }
 646  
 647    $node->status = 1;
 648    node_save($node);
 649  
 650    return TRUE;
 651  }
 652  
 653  /**
 654   * Prepare an error message for returning to the XMLRPC caller.
 655   */
 656  function blogapi_error($message) {
 657    static $xmlrpcusererr;
 658    if (!is_array($message)) {
 659      $message = array($message);
 660    }
 661  
 662    $message = implode(' ', $message);
 663  
 664    return xmlrpc_error($xmlrpcusererr + 1, strip_tags($message));
 665  }
 666  
 667  /**
 668   * Ensure that the given user has permission to edit a blog.
 669   */
 670  function blogapi_validate_user($username, $password) {
 671    global $user;
 672  
 673    $user = user_authenticate(array('name' => $username, 'pass' => $password));
 674  
 675    if ($user->uid) {
 676      if (user_access('administer content with blog api', $user)) {
 677        return $user;
 678      }
 679      else {
 680        return t('You do not have permission to edit this blog.');
 681      }
 682    }
 683    else {
 684      return t('Wrong username or password.');
 685    }
 686  }
 687  
 688  /**
 689   * For the blogger API, extract the node title from the contents field.
 690   */
 691  function blogapi_blogger_title(&$contents) {
 692    if (eregi('<title>([^<]*)</title>', $contents, $title)) {
 693      $title = strip_tags($title[0]);
 694      $contents = ereg_replace('<title>[^<]*</title>', '', $contents);
 695    }
 696    else {
 697      list($title, $contents) = explode("\n", $contents, 2);
 698    }
 699    return $title;
 700  }
 701  
 702  function blogapi_admin_settings() {
 703    $node_types = array_map('check_plain', node_get_types('names'));
 704    $defaults = isset($node_types['blog']) ? array('blog' => 1) : array();
 705    $form['blogapi_node_types'] = array(
 706      '#type' => 'checkboxes',
 707      '#title' => t('Enable for external blogging clients'),
 708      '#required' => TRUE,
 709      '#default_value' => variable_get('blogapi_node_types', $defaults),
 710      '#options' => $node_types,
 711      '#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.')
 712    );
 713  
 714    $blogapi_extensions_default = variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp');
 715    $blogapi_uploadsize_default = variable_get('blogapi_uploadsize_default', 1);
 716    $blogapi_usersize_default = variable_get('blogapi_usersize_default', 1);
 717  
 718    $form['settings_general'] = array(
 719      '#type' => 'fieldset',
 720      '#title' => t('File settings'),
 721      '#collapsible' => TRUE,
 722    );
 723  
 724    $form['settings_general']['blogapi_extensions_default'] = array(
 725      '#type' => 'textfield',
 726      '#title' => t('Default permitted file extensions'),
 727      '#default_value' => $blogapi_extensions_default,
 728      '#maxlength' => 255,
 729      '#description' => t('Default extensions that users can upload. Separate extensions with a space and do not include the leading dot.'),
 730    );
 731  
 732    $form['settings_general']['blogapi_uploadsize_default'] = array(
 733      '#type' => 'textfield',
 734      '#title' => t('Default maximum file size per upload'),
 735      '#default_value' => $blogapi_uploadsize_default,
 736      '#size' => 5,
 737      '#maxlength' => 5,
 738      '#description' => t('The default maximum file size a user can upload.'),
 739      '#field_suffix' => t('MB')
 740    );
 741  
 742    $form['settings_general']['blogapi_usersize_default'] = array(
 743      '#type' => 'textfield',
 744      '#title' => t('Default total file size per user'),
 745      '#default_value' => $blogapi_usersize_default,
 746      '#size' => 5,
 747      '#maxlength' => 5,
 748      '#description' => t('The default maximum size of all files a user can have on the site.'),
 749      '#field_suffix' => t('MB')
 750    );
 751  
 752    $form['settings_general']['upload_max_size'] = array('#value' => '<p>'. t('Your PHP settings limit the maximum file size per upload to %size.', array('%size' => format_size(file_upload_max_size()))).'</p>');
 753  
 754    $roles = user_roles(0, 'administer content with blog api');
 755    $form['roles'] = array('#type' => 'value', '#value' => $roles);
 756  
 757    foreach ($roles as $rid => $role) {
 758      $form['settings_role_'. $rid] = array(
 759        '#type' => 'fieldset',
 760        '#title' => t('Settings for @role', array('@role' => $role)),
 761        '#collapsible' => TRUE,
 762        '#collapsed' => TRUE,
 763      );
 764      $form['settings_role_'. $rid]['blogapi_extensions_'. $rid] = array(
 765        '#type' => 'textfield',
 766        '#title' => t('Permitted file extensions'),
 767        '#default_value' => variable_get('blogapi_extensions_'. $rid, $blogapi_extensions_default),
 768        '#maxlength' => 255,
 769        '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.'),
 770      );
 771      $form['settings_role_'. $rid]['blogapi_uploadsize_'. $rid] = array(
 772        '#type' => 'textfield',
 773        '#title' => t('Maximum file size per upload'),
 774        '#default_value' => variable_get('blogapi_uploadsize_'. $rid, $blogapi_uploadsize_default),
 775        '#size' => 5,
 776        '#maxlength' => 5,
 777        '#description' => t('The maximum size of a file a user can upload (in megabytes).'),
 778      );
 779      $form['settings_role_'. $rid]['blogapi_usersize_'. $rid] = array(
 780        '#type' => 'textfield',
 781        '#title' => t('Total file size per user'),
 782        '#default_value' => variable_get('blogapi_usersize_'. $rid, $blogapi_usersize_default),
 783        '#size' => 5,
 784        '#maxlength' => 5,
 785        '#description' => t('The maximum size of all files a user can have on the site (in megabytes).'),
 786      );
 787    }
 788  
 789    return system_settings_form($form);
 790  }
 791  
 792  function blogapi_menu() {
 793    $items['blogapi/rsd'] = array(
 794      'title' => 'RSD',
 795      'page callback' => 'blogapi_rsd',
 796      'access arguments' => array('access content'),
 797      'type' => MENU_CALLBACK,
 798    );
 799    $items['admin/settings/blogapi'] = array(
 800      'title' => 'Blog API',
 801      'description' => 'Configure the content types available to external blogging clients.',
 802      'page callback' => 'drupal_get_form',
 803      'page arguments' => array('blogapi_admin_settings'),
 804      'access arguments' => array('administer site configuration'),
 805      'type' => MENU_NORMAL_ITEM,
 806    );
 807  
 808    return $items;
 809  }
 810  
 811  function blogapi_init() {
 812    if (drupal_is_front_page()) {
 813      drupal_add_link(array('rel' => 'EditURI',
 814                            'type' => 'application/rsd+xml',
 815                            'title' => t('RSD'),
 816                            'href' => url('blogapi/rsd', array('absolute' => TRUE))));
 817    }
 818  }
 819  
 820  function blogapi_rsd() {
 821    global $base_url;
 822  
 823    $xmlrpc = $base_url .'/xmlrpc.php';
 824    $base = url('', array('absolute' => TRUE));
 825    $blogid = 1; # until we figure out how to handle multiple bloggers
 826  
 827    drupal_set_header('Content-Type: application/rsd+xml; charset=utf-8');
 828    print <<<__RSD__
 829  <?xml version="1.0"?>
 830  <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
 831    <service>
 832      <engineName>Drupal</engineName>
 833      <engineLink>http://drupal.org/</engineLink>
 834      <homePageLink>$base</homePageLink>
 835      <apis>
 836        <api name="MetaWeblog" preferred="false" apiLink="$xmlrpc" blogID="$blogid" />
 837        <api name="Blogger" preferred="false" apiLink="$xmlrpc" blogID="$blogid" />
 838        <api name="MovableType" preferred="true" apiLink="$xmlrpc" blogID="$blogid" />
 839      </apis>
 840    </service>
 841  </rsd>
 842  __RSD__;
 843  }
 844  
 845  /**
 846   * Handles extra information sent by clients according to MovableType's spec.
 847   */
 848  function _blogapi_mt_extra(&$node, $struct) {
 849    if (is_array($node)) {
 850      $was_array = TRUE;
 851      $node = (object)$node;
 852    }
 853  
 854    // mt_allow_comments
 855    if (array_key_exists('mt_allow_comments', $struct)) {
 856      switch ($struct['mt_allow_comments']) {
 857        case 0:
 858          $node->comment = COMMENT_NODE_DISABLED;
 859          break;
 860        case 1:
 861          $node->comment = COMMENT_NODE_READ_WRITE;
 862          break;
 863        case 2:
 864          $node->comment = COMMENT_NODE_READ_ONLY;
 865          break;
 866      }
 867    }
 868  
 869    // merge the 3 body sections (description, mt_excerpt, mt_text_more) into
 870    // one body
 871    if ($struct['mt_excerpt']) {
 872      $node->body = $struct['mt_excerpt'] .'<!--break-->'. $node->body;
 873    }
 874    if ($struct['mt_text_more']) {
 875      $node->body = $node->body .'<!--extended-->'. $struct['mt_text_more'];
 876    }
 877  
 878    // mt_convert_breaks
 879    if ($struct['mt_convert_breaks']) {
 880      $node->format = $struct['mt_convert_breaks'];
 881    }
 882  
 883    // dateCreated
 884    if ($struct['dateCreated']) {
 885      $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');
 886    }
 887  
 888    if ($was_array) {
 889      $node = (array)$node;
 890    }
 891  }
 892  
 893  function _blogapi_get_post($node, $bodies = TRUE) {
 894    $xmlrpcval = array(
 895      'userid' => $node->name,
 896      'dateCreated' => xmlrpc_date($node->created),
 897      'title' => $node->title,
 898      'postid' => $node->nid,
 899      'link' => url('node/'. $node->nid, array('absolute' => TRUE)),
 900      'permaLink' => url('node/'. $node->nid, array('absolute' => TRUE)),
 901    );
 902    if ($bodies) {
 903      if ($node->comment == 1) {
 904        $comment = 2;
 905      }
 906      else if ($node->comment == 2) {
 907        $comment = 1;
 908      }
 909      $xmlrpcval['content'] = "<title>$node->title</title>$node->body";
 910      $xmlrpcval['description'] = $node->body;
 911      // Add MT specific fields
 912      $xmlrpcval['mt_allow_comments'] = (int) $comment;
 913      $xmlrpcval['mt_convert_breaks'] = $node->format;
 914    }
 915  
 916    return $xmlrpcval;
 917  }
 918  
 919  /**
 920   * Validate blog ID, which maps to a content type in Drupal.
 921   *
 922   * Only content types configured to work with Blog API are supported.
 923   *
 924   * @return
 925   *   TRUE if the content type is supported and the user has permission
 926   *   to post, or a blogapi_error() XML construct otherwise.
 927   */
 928  function _blogapi_validate_blogid($blogid) {
 929    $types = _blogapi_get_node_types();
 930    if (in_array($blogid, $types, TRUE)) {
 931      return TRUE;
 932    }
 933    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)));
 934  }
 935  
 936  function _blogapi_get_node_types() {
 937    $available_types = array_keys(array_filter(variable_get('blogapi_node_types', array('blog' => 1))));
 938    $types = array();
 939    foreach (node_get_types() as $type => $name) {
 940      if (node_access('create', $type) && in_array($type, $available_types)) {
 941        $types[] = $type;
 942      }
 943    }
 944  
 945    return $types;
 946  }
 947  
 948  function _blogapi_space_used($uid) {
 949    return db_result(db_query('SELECT SUM(filesize) FROM {blogapi_files} f WHERE f.uid = %d', $uid));
 950  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7