| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Jul 9 18:01:44 2012 | Cross-referenced by PHPXref 0.7 |