[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

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

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


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7