Projects : mp-wp : mp-wp_genesis

mp-wp/xmlrpc.php

Dir - Raw

1<?php
2/**
3 * XML-RPC protocol support for WordPress
4 *
5 * @license GPL v2 <./license.txt>
6 * @package WordPress
7 */
8
9/**
10 * Whether this is a XMLRPC Request
11 *
12 * @var bool
13 */
14define('XMLRPC_REQUEST', true);
15
16// Some browser-embedded clients send cookies. We don't want them.
17$_COOKIE = array();
18
19// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
20// but we can do it ourself.
21if ( !isset( $HTTP_RAW_POST_DATA ) ) {
22 $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
23}
24
25// fix for mozBlog and other cases where '<?xml' isn't on the very first line
26if ( isset($HTTP_RAW_POST_DATA) )
27 $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
28
29/** Include the bootstrap for setting up WordPress environment */
30include('./wp-load.php');
31
32if ( isset( $_GET['rsd'] ) ) { // http://archipelago.phrasewise.com/rsd
33header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
34?>
35<?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
36<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
37 <service>
38 <engineName>WordPress</engineName>
39 <engineLink>http://wordpress.org/</engineLink>
40 <homePageLink><?php bloginfo_rss('url') ?></homePageLink>
41 <apis>
42 <api name="WordPress" blogID="1" preferred="true" apiLink="<?php echo site_url('xmlrpc.php') ?>" />
43 <api name="Movable Type" blogID="1" preferred="false" apiLink="<?php echo site_url('xmlrpc.php') ?>" />
44 <api name="MetaWeblog" blogID="1" preferred="false" apiLink="<?php echo site_url('xmlrpc.php') ?>" />
45 <api name="Blogger" blogID="1" preferred="false" apiLink="<?php echo site_url('xmlrpc.php') ?>" />
46 <api name="Atom" blogID="" preferred="false" apiLink="<?php echo apply_filters('atom_service_url', site_url('wp-app.php/service') ) ?>" />
47 </apis>
48 </service>
49</rsd>
50<?php
51exit;
52}
53
54include_once(ABSPATH . 'wp-admin/includes/admin.php');
55include_once(ABSPATH . WPINC . '/class-IXR.php');
56
57// Turn off all warnings and errors.
58// error_reporting(0);
59
60/**
61 * Posts submitted via the xmlrpc interface get that title
62 * @name post_default_title
63 * @var string
64 */
65$post_default_title = "";
66
67/**
68 * Whether to enable XMLRPC Logging.
69 *
70 * @name xmlrpc_logging
71 * @var int|bool
72 */
73$xmlrpc_logging = 0;
74
75/**
76 * logIO() - Writes logging info to a file.
77 *
78 * @uses $xmlrpc_logging
79 * @package WordPress
80 * @subpackage Logging
81 *
82 * @param string $io Whether input or output
83 * @param string $msg Information describing logging reason.
84 * @return bool Always return true
85 */
86function logIO($io,$msg) {
87 global $xmlrpc_logging;
88 if ($xmlrpc_logging) {
89 $fp = fopen("../xmlrpc.log","a+");
90 $date = gmdate("Y-m-d H:i:s ");
91 $iot = ($io == "I") ? " Input: " : " Output: ";
92 fwrite($fp, "\n\n".$date.$iot.$msg);
93 fclose($fp);
94 }
95 return true;
96}
97
98if ( isset($HTTP_RAW_POST_DATA) )
99 logIO("I", $HTTP_RAW_POST_DATA);
100
101/**
102 * WordPress XMLRPC server implementation.
103 *
104 * Implements compatability for Blogger API, MetaWeblog API, MovableType, and
105 * pingback. Additional WordPress API for managing comments, pages, posts,
106 * options, etc.
107 *
108 * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the
109 * administration panels.
110 *
111 * @package WordPress
112 * @subpackage Publishing
113 * @since 1.5.0
114 */
115class wp_xmlrpc_server extends IXR_Server {
116
117 /**
118 * Register all of the XMLRPC methods that XMLRPC server understands.
119 *
120 * PHP4 constructor and sets up server and method property. Passes XMLRPC
121 * methods through the 'xmlrpc_methods' filter to allow plugins to extend
122 * or replace XMLRPC methods.
123 *
124 * @since 1.5.0
125 *
126 * @return wp_xmlrpc_server
127 */
128 function wp_xmlrpc_server() {
129 $this->methods = array(
130// Close all but pingback!
131/*
132 // WordPress API
133 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
134 'wp.getPage' => 'this:wp_getPage',
135 'wp.getPages' => 'this:wp_getPages',
136 'wp.newPage' => 'this:wp_newPage',
137 'wp.deletePage' => 'this:wp_deletePage',
138 'wp.editPage' => 'this:wp_editPage',
139 'wp.getPageList' => 'this:wp_getPageList',
140 'wp.getAuthors' => 'this:wp_getAuthors',
141 'wp.getCategories' => 'this:mw_getCategories', // Alias
142 'wp.getTags' => 'this:wp_getTags',
143 'wp.newCategory' => 'this:wp_newCategory',
144 'wp.deleteCategory' => 'this:wp_deleteCategory',
145 'wp.suggestCategories' => 'this:wp_suggestCategories',
146 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias
147 'wp.getCommentCount' => 'this:wp_getCommentCount',
148 'wp.getPostStatusList' => 'this:wp_getPostStatusList',
149 'wp.getPageStatusList' => 'this:wp_getPageStatusList',
150 'wp.getPageTemplates' => 'this:wp_getPageTemplates',
151 'wp.getOptions' => 'this:wp_getOptions',
152 'wp.setOptions' => 'this:wp_setOptions',
153 'wp.getComment' => 'this:wp_getComment',
154 'wp.getComments' => 'this:wp_getComments',
155 'wp.deleteComment' => 'this:wp_deleteComment',
156 'wp.editComment' => 'this:wp_editComment',
157 'wp.newComment' => 'this:wp_newComment',
158 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
159
160 // Blogger API
161 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
162 'blogger.getUserInfo' => 'this:blogger_getUserInfo',
163 'blogger.getPost' => 'this:blogger_getPost',
164 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
165 'blogger.getTemplate' => 'this:blogger_getTemplate',
166 'blogger.setTemplate' => 'this:blogger_setTemplate',
167 'blogger.newPost' => 'this:blogger_newPost',
168 'blogger.editPost' => 'this:blogger_editPost',
169 'blogger.deletePost' => 'this:blogger_deletePost',
170
171 // MetaWeblog API (with MT extensions to structs)
172 'metaWeblog.newPost' => 'this:mw_newPost',
173 'metaWeblog.editPost' => 'this:mw_editPost',
174 'metaWeblog.getPost' => 'this:mw_getPost',
175 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
176 'metaWeblog.getCategories' => 'this:mw_getCategories',
177 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
178
179 // MetaWeblog API aliases for Blogger API
180 // see http://www.xmlrpc.com/stories/storyReader$2460
181 'metaWeblog.deletePost' => 'this:blogger_deletePost',
182 'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
183 'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
184 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
185
186 // MovableType API
187 'mt.getCategoryList' => 'this:mt_getCategoryList',
188 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
189 'mt.getPostCategories' => 'this:mt_getPostCategories',
190 'mt.setPostCategories' => 'this:mt_setPostCategories',
191 'mt.supportedMethods' => 'this:mt_supportedMethods',
192 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
193 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
194 'mt.publishPost' => 'this:mt_publishPost',
195
196
197 'demo.sayHello' => 'this:sayHello',
198 'demo.addTwoNumbers' => 'this:addTwoNumbers'
199*/
200
201 // PingBack
202 'pingback.ping' => 'this:pingback_ping',
203 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
204
205 );
206
207 $this->initialise_blog_option_info( );
208 $this->methods = apply_filters('xmlrpc_methods', $this->methods);
209 $this->IXR_Server($this->methods);
210 }
211
212 /**
213 * Test XMLRPC API by saying, "Hello!" to client.
214 *
215 * @since 1.5.0
216 *
217 * @param array $args Method Parameters.
218 * @return string
219 */
220 function sayHello($args) {
221 return 'Hello!';
222 }
223
224 /**
225 * Test XMLRPC API by adding two numbers for client.
226 *
227 * @since 1.5.0
228 *
229 * @param array $args Method Parameters.
230 * @return int
231 */
232 function addTwoNumbers($args) {
233 $number1 = $args[0];
234 $number2 = $args[1];
235 return $number1 + $number2;
236 }
237
238 /**
239 * Check user's credentials.
240 *
241 * @since 1.5.0
242 *
243 * @param string $user_login User's username.
244 * @param string $user_pass User's password.
245 * @return bool Whether authentication passed.
246 */
247 function login_pass_ok($user_login, $user_pass) {
248 if ( !get_option( 'enable_xmlrpc' ) ) {
249 $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this blog. An admin user can enable them at %s'), admin_url('options-writing.php') ) );
250 return false;
251 }
252
253 if (!user_pass_ok($user_login, $user_pass)) {
254 $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
255 return false;
256 }
257 return true;
258 }
259
260 /**
261 * Sanitize string or array of strings for database.
262 *
263 * @since 1.5.2
264 *
265 * @param string|array $array Sanitize single string or array of strings.
266 * @return string|array Type matches $array and sanitized for the database.
267 */
268 function escape(&$array) {
269 global $wpdb;
270
271 if(!is_array($array)) {
272 return($wpdb->escape($array));
273 }
274 else {
275 foreach ( (array) $array as $k => $v ) {
276 if (is_array($v)) {
277 $this->escape($array[$k]);
278 } else if (is_object($v)) {
279 //skip
280 } else {
281 $array[$k] = $wpdb->escape($v);
282 }
283 }
284 }
285 }
286
287 /**
288 * Retrieve custom fields for post.
289 *
290 * @since 2.5.0
291 *
292 * @param int $post_id Post ID.
293 * @return array Custom fields, if exist.
294 */
295 function get_custom_fields($post_id) {
296 $post_id = (int) $post_id;
297
298 $custom_fields = array();
299
300 foreach ( (array) has_meta($post_id) as $meta ) {
301 // Don't expose protected fields.
302 if ( strpos($meta['meta_key'], '_wp_') === 0 ) {
303 continue;
304 }
305
306 $custom_fields[] = array(
307 "id" => $meta['meta_id'],
308 "key" => $meta['meta_key'],
309 "value" => $meta['meta_value']
310 );
311 }
312
313 return $custom_fields;
314 }
315
316 /**
317 * Set custom fields for post.
318 *
319 * @since 2.5.0
320 *
321 * @param int $post_id Post ID.
322 * @param array $fields Custom fields.
323 */
324 function set_custom_fields($post_id, $fields) {
325 $post_id = (int) $post_id;
326
327 foreach ( (array) $fields as $meta ) {
328 if ( isset($meta['id']) ) {
329 $meta['id'] = (int) $meta['id'];
330
331 if ( isset($meta['key']) ) {
332 update_meta($meta['id'], $meta['key'], $meta['value']);
333 }
334 else {
335 delete_meta($meta['id']);
336 }
337 }
338 else {
339 $_POST['metakeyinput'] = $meta['key'];
340 $_POST['metavalue'] = $meta['value'];
341 add_meta($post_id);
342 }
343 }
344 }
345
346 /**
347 * Setup blog options property.
348 *
349 * Passes property through 'xmlrpc_blog_options' filter.
350 *
351 * @since 2.6.0
352 */
353 function initialise_blog_option_info( ) {
354 global $wp_version;
355
356 $this->blog_options = array(
357 // Read only options
358 'software_name' => array(
359 'desc' => __( 'Software Name' ),
360 'readonly' => true,
361 'value' => 'WordPress'
362 ),
363 'software_version' => array(
364 'desc' => __( 'Software Version' ),
365 'readonly' => true,
366 'value' => $wp_version
367 ),
368 'blog_url' => array(
369 'desc' => __( 'Blog URL' ),
370 'readonly' => true,
371 'option' => 'siteurl'
372 ),
373
374 // Updatable options
375 'time_zone' => array(
376 'desc' => __( 'Time Zone' ),
377 'readonly' => false,
378 'option' => 'gmt_offset'
379 ),
380 'blog_title' => array(
381 'desc' => __( 'Blog Title' ),
382 'readonly' => false,
383 'option' => 'blogname'
384 ),
385 'blog_tagline' => array(
386 'desc' => __( 'Blog Tagline' ),
387 'readonly' => false,
388 'option' => 'blogdescription'
389 ),
390 'date_format' => array(
391 'desc' => __( 'Date Format' ),
392 'readonly' => false,
393 'option' => 'date_format'
394 ),
395 'time_format' => array(
396 'desc' => __( 'Time Format' ),
397 'readonly' => false,
398 'option' => 'time_format'
399 )
400 );
401
402 $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
403 }
404
405 /**
406 * Retrieve the blogs of the user.
407 *
408 * @since 2.6.0
409 *
410 * @param array $args Method parameters.
411 * @return array
412 */
413 function wp_getUsersBlogs( $args ) {
414 // If this isn't on WPMU then just use blogger_getUsersBlogs
415 if( !function_exists( 'is_site_admin' ) ) {
416 array_unshift( $args, 1 );
417 return $this->blogger_getUsersBlogs( $args );
418 }
419
420 $this->escape( $args );
421
422 $username = $args[0];
423 $password = $args[1];
424
425 if( !$this->login_pass_ok( $username, $password ) )
426 return $this->error;
427
428 do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
429
430 $user = set_current_user( 0, $username );
431
432 $blogs = (array) get_blogs_of_user( $user->ID );
433 $struct = array( );
434
435 foreach( $blogs as $blog ) {
436 // Don't include blogs that aren't hosted at this site
437 if( $blog->site_id != $current_site->id )
438 continue;
439
440 $blog_id = $blog->userblog_id;
441 switch_to_blog($blog_id);
442 $is_admin = current_user_can('level_8');
443
444 $struct[] = array(
445 'isAdmin' => $is_admin,
446 'url' => get_option( 'home' ) . '/',
447 'blogid' => $blog_id,
448 'blogName' => get_option( 'blogname' ),
449 'xmlrpc' => get_option( 'home' ) . '/xmlrpc.php'
450 );
451
452 restore_current_blog( );
453 }
454
455 return $struct;
456 }
457
458 /**
459 * Retrieve page.
460 *
461 * @since 2.2.0
462 *
463 * @param array $args Method parameters.
464 * @return array
465 */
466 function wp_getPage($args) {
467 $this->escape($args);
468
469 $blog_id = (int) $args[0];
470 $page_id = (int) $args[1];
471 $username = $args[2];
472 $password = $args[3];
473
474 if(!$this->login_pass_ok($username, $password)) {
475 return($this->error);
476 }
477
478 set_current_user( 0, $username );
479 if( !current_user_can( 'edit_page', $page_id ) )
480 return new IXR_Error( 401, __( 'Sorry, you can not edit this page.' ) );
481
482 do_action('xmlrpc_call', 'wp.getPage');
483
484 // Lookup page info.
485 $page = get_page($page_id);
486
487 // If we found the page then format the data.
488 if($page->ID && ($page->post_type == "page")) {
489 // Get all of the page content and link.
490 $full_page = get_extended($page->post_content);
491 $link = post_permalink($page->ID);
492
493 // Get info the page parent if there is one.
494 $parent_title = "";
495 if(!empty($page->post_parent)) {
496 $parent = get_page($page->post_parent);
497 $parent_title = $parent->post_title;
498 }
499
500 // Determine comment and ping settings.
501 $allow_comments = ("open" == $page->comment_status) ? 1 : 0;
502 $allow_pings = ("open" == $page->ping_status) ? 1 : 0;
503
504 // Format page date.
505 $page_date = mysql2date("Ymd\TH:i:s", $page->post_date);
506 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt);
507
508 // Pull the categories info together.
509 $categories = array();
510 foreach(wp_get_post_categories($page->ID) as $cat_id) {
511 $categories[] = get_cat_name($cat_id);
512 }
513
514 // Get the author info.
515 $author = get_userdata($page->post_author);
516
517 $page_template = get_post_meta( $page->ID, '_wp_page_template', true );
518 if( empty( $page_template ) )
519 $page_template = 'default';
520
521 $page_struct = array(
522 "dateCreated" => new IXR_Date($page_date),
523 "userid" => $page->post_author,
524 "page_id" => $page->ID,
525 "page_status" => $page->post_status,
526 "description" => $full_page["main"],
527 "title" => $page->post_title,
528 "link" => $link,
529 "permaLink" => $link,
530 "categories" => $categories,
531 "excerpt" => $page->post_excerpt,
532 "text_more" => $full_page["extended"],
533 "mt_allow_comments" => $allow_comments,
534 "mt_allow_pings" => $allow_pings,
535 "wp_slug" => $page->post_name,
536 "wp_password" => $page->post_password,
537 "wp_author" => $author->display_name,
538 "wp_page_parent_id" => $page->post_parent,
539 "wp_page_parent_title" => $parent_title,
540 "wp_page_order" => $page->menu_order,
541 "wp_author_id" => $author->ID,
542 "wp_author_display_name" => $author->display_name,
543 "date_created_gmt" => new IXR_Date($page_date_gmt),
544 "custom_fields" => $this->get_custom_fields($page_id),
545 "wp_page_template" => $page_template
546 );
547
548 return($page_struct);
549 }
550 // If the page doesn't exist indicate that.
551 else {
552 return(new IXR_Error(404, __("Sorry, no such page.")));
553 }
554 }
555
556 /**
557 * Retrieve Pages.
558 *
559 * @since 2.2.0
560 *
561 * @param array $args Method parameters.
562 * @return array
563 */
564 function wp_getPages($args) {
565 $this->escape($args);
566
567 $blog_id = (int) $args[0];
568 $username = $args[1];
569 $password = $args[2];
570 $num_pages = (int) $args[3];
571
572 if(!$this->login_pass_ok($username, $password)) {
573 return($this->error);
574 }
575
576 set_current_user( 0, $username );
577 if( !current_user_can( 'edit_pages' ) )
578 return new IXR_Error( 401, __( 'Sorry, you can not edit pages.' ) );
579
580 do_action('xmlrpc_call', 'wp.getPages');
581
582 $page_limit = 10;
583 if( isset( $num_pages ) ) {
584 $page_limit = $num_pages;
585 }
586
587 $pages = get_posts( "post_type=page&post_status=all&numberposts={$page_limit}" );
588 $num_pages = count($pages);
589
590 // If we have pages, put together their info.
591 if($num_pages >= 1) {
592 $pages_struct = array();
593
594 for($i = 0; $i < $num_pages; $i++) {
595 $page = wp_xmlrpc_server::wp_getPage(array(
596 $blog_id, $pages[$i]->ID, $username, $password
597 ));
598 $pages_struct[] = $page;
599 }
600
601 return($pages_struct);
602 }
603 // If no pages were found return an error.
604 else {
605 return(array());
606 }
607 }
608
609 /**
610 * Create new page.
611 *
612 * @since 2.2.0
613 *
614 * @param array $args Method parameters.
615 * @return unknown
616 */
617 function wp_newPage($args) {
618 // Items not escaped here will be escaped in newPost.
619 $username = $this->escape($args[1]);
620 $password = $this->escape($args[2]);
621 $page = $args[3];
622 $publish = $args[4];
623
624 if(!$this->login_pass_ok($username, $password)) {
625 return($this->error);
626 }
627
628 do_action('xmlrpc_call', 'wp.newPage');
629
630 // Set the user context and check if they are allowed
631 // to add new pages.
632 $user = set_current_user(0, $username);
633 if(!current_user_can("publish_pages")) {
634 return(new IXR_Error(401, __("Sorry, you can not add new pages.")));
635 }
636
637 // Mark this as content for a page.
638 $args[3]["post_type"] = "page";
639
640 // Let mw_newPost do all of the heavy lifting.
641 return($this->mw_newPost($args));
642 }
643
644 /**
645 * Delete page.
646 *
647 * @since 2.2.0
648 *
649 * @param array $args Method parameters.
650 * @return bool True, if success.
651 */
652 function wp_deletePage($args) {
653 $this->escape($args);
654
655 $blog_id = (int) $args[0];
656 $username = $args[1];
657 $password = $args[2];
658 $page_id = (int) $args[3];
659
660 if(!$this->login_pass_ok($username, $password)) {
661 return($this->error);
662 }
663
664 do_action('xmlrpc_call', 'wp.deletePage');
665
666 // Get the current page based on the page_id and
667 // make sure it is a page and not a post.
668 $actual_page = wp_get_single_post($page_id, ARRAY_A);
669 if(
670 !$actual_page
671 || ($actual_page["post_type"] != "page")
672 ) {
673 return(new IXR_Error(404, __("Sorry, no such page.")));
674 }
675
676 // Set the user context and make sure they can delete pages.
677 set_current_user(0, $username);
678 if(!current_user_can("delete_page", $page_id)) {
679 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));
680 }
681
682 // Attempt to delete the page.
683 $result = wp_delete_post($page_id);
684 if(!$result) {
685 return(new IXR_Error(500, __("Failed to delete the page.")));
686 }
687
688 return(true);
689 }
690
691 /**
692 * Edit page.
693 *
694 * @since 2.2.0
695 *
696 * @param array $args Method parameters.
697 * @return unknown
698 */
699 function wp_editPage($args) {
700 // Items not escaped here will be escaped in editPost.
701 $blog_id = (int) $args[0];
702 $page_id = (int) $this->escape($args[1]);
703 $username = $this->escape($args[2]);
704 $password = $this->escape($args[3]);
705 $content = $args[4];
706 $publish = $args[5];
707
708 if(!$this->login_pass_ok($username, $password)) {
709 return($this->error);
710 }
711
712 do_action('xmlrpc_call', 'wp.editPage');
713
714 // Get the page data and make sure it is a page.
715 $actual_page = wp_get_single_post($page_id, ARRAY_A);
716 if(
717 !$actual_page
718 || ($actual_page["post_type"] != "page")
719 ) {
720 return(new IXR_Error(404, __("Sorry, no such page.")));
721 }
722
723 // Set the user context and make sure they are allowed to edit pages.
724 set_current_user(0, $username);
725 if(!current_user_can("edit_page", $page_id)) {
726 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));
727 }
728
729 // Mark this as content for a page.
730 $content["post_type"] = "page";
731
732 // Arrange args in the way mw_editPost understands.
733 $args = array(
734 $page_id,
735 $username,
736 $password,
737 $content,
738 $publish
739 );
740
741 // Let mw_editPost do all of the heavy lifting.
742 return($this->mw_editPost($args));
743 }
744
745 /**
746 * Retrieve page list.
747 *
748 * @since 2.2.0
749 *
750 * @param array $args Method parameters.
751 * @return unknown
752 */
753 function wp_getPageList($args) {
754 global $wpdb;
755
756 $this->escape($args);
757
758 $blog_id = (int) $args[0];
759 $username = $args[1];
760 $password = $args[2];
761
762 if(!$this->login_pass_ok($username, $password)) {
763 return($this->error);
764 }
765
766 set_current_user( 0, $username );
767 if( !current_user_can( 'edit_pages' ) )
768 return new IXR_Error( 401, __( 'Sorry, you can not edit pages.' ) );
769
770 do_action('xmlrpc_call', 'wp.getPageList');
771
772 // Get list of pages ids and titles
773 $page_list = $wpdb->get_results("
774 SELECT ID page_id,
775 post_title page_title,
776 post_parent page_parent_id,
777 post_date_gmt,
778 post_date
779 FROM {$wpdb->posts}
780 WHERE post_type = 'page'
781 ORDER BY ID
782 ");
783
784 // The date needs to be formated properly.
785 $num_pages = count($page_list);
786 for($i = 0; $i < $num_pages; $i++) {
787 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date);
788 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt);
789
790 $page_list[$i]->dateCreated = new IXR_Date($post_date);
791 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt);
792
793 unset($page_list[$i]->post_date_gmt);
794 unset($page_list[$i]->post_date);
795 }
796
797 return($page_list);
798 }
799
800 /**
801 * Retrieve authors list.
802 *
803 * @since 2.2.0
804 *
805 * @param array $args Method parameters.
806 * @return array
807 */
808 function wp_getAuthors($args) {
809
810 $this->escape($args);
811
812 $blog_id = (int) $args[0];
813 $username = $args[1];
814 $password = $args[2];
815
816 if(!$this->login_pass_ok($username, $password)) {
817 return($this->error);
818 }
819
820 set_current_user(0, $username);
821 if(!current_user_can("edit_posts")) {
822 return(new IXR_Error(401, __("Sorry, you can not edit posts on this blog.")));
823 }
824
825 do_action('xmlrpc_call', 'wp.getAuthors');
826
827 $authors = array();
828 foreach( (array) get_users_of_blog() as $row ) {
829 $authors[] = array(
830 "user_id" => $row->user_id,
831 "user_login" => $row->user_login,
832 "display_name" => $row->display_name
833 );
834 }
835
836 return($authors);
837 }
838
839 /**
840 * Get list of all tags
841 *
842 * @since 2.7
843 *
844 * @param array $args Method parameters.
845 * @return array
846 */
847 function wp_getTags( $args ) {
848 $this->escape( $args );
849
850 $blog_id = (int) $args[0];
851 $username = $args[1];
852 $password = $args[2];
853
854 if( !$this->login_pass_ok( $username, $password ) ) {
855 return $this->error;
856 }
857
858 set_current_user( 0, $username );
859 if( !current_user_can( 'edit_posts' ) ) {
860 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this blog in order to view tags.' ) );
861 }
862
863 do_action( 'xmlrpc_call', 'wp.getKeywords' );
864
865 $tags = array( );
866
867 if( $all_tags = get_tags( ) ) {
868 foreach( (array) $all_tags as $tag ) {
869 $struct['tag_id'] = $tag->term_id;
870 $struct['name'] = $tag->name;
871 $struct['count'] = $tag->count;
872 $struct['slug'] = $tag->slug;
873 $struct['html_url'] = wp_specialchars( get_tag_link( $tag->term_id ) );
874 $struct['rss_url'] = wp_specialchars( get_tag_feed_link( $tag->term_id ) );
875
876 $tags[] = $struct;
877 }
878 }
879
880 return $tags;
881 }
882
883 /**
884 * Create new category.
885 *
886 * @since 2.2.0
887 *
888 * @param array $args Method parameters.
889 * @return int Category ID.
890 */
891 function wp_newCategory($args) {
892 $this->escape($args);
893
894 $blog_id = (int) $args[0];
895 $username = $args[1];
896 $password = $args[2];
897 $category = $args[3];
898
899 if(!$this->login_pass_ok($username, $password)) {
900 return($this->error);
901 }
902
903 do_action('xmlrpc_call', 'wp.newCategory');
904
905 // Set the user context and make sure they are
906 // allowed to add a category.
907 set_current_user(0, $username);
908 if(!current_user_can("manage_categories")) {
909 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category.")));
910 }
911
912 // If no slug was provided make it empty so that
913 // WordPress will generate one.
914 if(empty($category["slug"])) {
915 $category["slug"] = "";
916 }
917
918 // If no parent_id was provided make it empty
919 // so that it will be a top level page (no parent).
920 if ( !isset($category["parent_id"]) )
921 $category["parent_id"] = "";
922
923 // If no description was provided make it empty.
924 if(empty($category["description"])) {
925 $category["description"] = "";
926 }
927
928 $new_category = array(
929 "cat_name" => $category["name"],
930 "category_nicename" => $category["slug"],
931 "category_parent" => $category["parent_id"],
932 "category_description" => $category["description"]
933 );
934
935 $cat_id = wp_insert_category($new_category);
936 if(!$cat_id) {
937 return(new IXR_Error(500, __("Sorry, the new category failed.")));
938 }
939
940 return($cat_id);
941 }
942
943 /**
944 * Remove category.
945 *
946 * @since 2.5.0
947 *
948 * @param array $args Method parameters.
949 * @return mixed See {@link wp_delete_category()} for return info.
950 */
951 function wp_deleteCategory($args) {
952 $this->escape($args);
953
954 $blog_id = (int) $args[0];
955 $username = $args[1];
956 $password = $args[2];
957 $category_id = (int) $args[3];
958
959 if( !$this->login_pass_ok( $username, $password ) ) {
960 return $this->error;
961 }
962
963 do_action('xmlrpc_call', 'wp.deleteCategory');
964
965 set_current_user(0, $username);
966 if( !current_user_can("manage_categories") ) {
967 return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) );
968 }
969
970 return wp_delete_category( $category_id );
971 }
972
973 /**
974 * Retrieve category list.
975 *
976 * @since 2.2.0
977 *
978 * @param array $args Method parameters.
979 * @return array
980 */
981 function wp_suggestCategories($args) {
982 $this->escape($args);
983
984 $blog_id = (int) $args[0];
985 $username = $args[1];
986 $password = $args[2];
987 $category = $args[3];
988 $max_results = (int) $args[4];
989
990 if(!$this->login_pass_ok($username, $password)) {
991 return($this->error);
992 }
993
994 set_current_user(0, $username);
995 if( !current_user_can( 'edit_posts' ) )
996 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this blog in order to view categories.' ) );
997
998 do_action('xmlrpc_call', 'wp.suggestCategories');
999
1000 $category_suggestions = array();
1001 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);
1002 foreach ( (array) get_categories($args) as $cat ) {
1003 $category_suggestions[] = array(
1004 "category_id" => $cat->cat_ID,
1005 "category_name" => $cat->cat_name
1006 );
1007 }
1008
1009 return($category_suggestions);
1010 }
1011
1012 /**
1013 * Retrieve comment.
1014 *
1015 * @since 2.7.0
1016 *
1017 * @param array $args Method parameters.
1018 * @return array
1019 */
1020 function wp_getComment($args) {
1021 $this->escape($args);
1022
1023 $blog_id = (int) $args[0];
1024 $username = $args[1];
1025 $password = $args[2];
1026 $comment_id = (int) $args[3];
1027
1028 if ( !$this->login_pass_ok( $username, $password ) )
1029 return $this->error;
1030
1031 set_current_user( 0, $username );
1032 if ( !current_user_can( 'moderate_comments' ) )
1033 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this blog.' ) );
1034
1035 do_action('xmlrpc_call', 'wp.getComment');
1036
1037 if ( ! $comment = get_comment($comment_id) )
1038 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
1039
1040 // Format page date.
1041 $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date);
1042 $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt);
1043
1044 if ( 0 == $comment->comment_approved )
1045 $comment_status = 'hold';
1046 else if ( 'spam' == $comment->comment_approved )
1047 $comment_status = 'spam';
1048 else if ( 1 == $comment->comment_approved )
1049 $comment_status = 'approve';
1050 else
1051 $comment_status = $comment->comment_approved;
1052
1053 $link = get_comment_link($comment);
1054
1055 $comment_struct = array(
1056 "date_created_gmt" => new IXR_Date($comment_date_gmt),
1057 "user_id" => $comment->user_id,
1058 "comment_id" => $comment->comment_ID,
1059 "parent" => $comment->comment_parent,
1060 "status" => $comment_status,
1061 "content" => $comment->comment_content,
1062 "link" => $link,
1063 "post_id" => $comment->comment_post_ID,
1064 "post_title" => get_the_title($comment->comment_post_ID),
1065 "author" => $comment->comment_author,
1066 "author_url" => $comment->comment_author_url,
1067 "author_email" => $comment->comment_author_email,
1068 "author_ip" => $comment->comment_author_IP,
1069 "type" => $comment->comment_type,
1070 );
1071
1072 return $comment_struct;
1073 }
1074
1075 /**
1076 * Retrieve comments.
1077 *
1078 * @since 2.7.0
1079 *
1080 * @param array $args Method parameters.
1081 * @return array
1082 */
1083 function wp_getComments($args) {
1084 $this->escape($args);
1085
1086 $blog_id = (int) $args[0];
1087 $username = $args[1];
1088 $password = $args[2];
1089 $struct = $args[3];
1090
1091 if ( !$this->login_pass_ok($username, $password) )
1092 return($this->error);
1093
1094 set_current_user( 0, $username );
1095 if ( !current_user_can( 'moderate_comments' ) )
1096 return new IXR_Error( 401, __( 'Sorry, you can not edit comments.' ) );
1097
1098 do_action('xmlrpc_call', 'wp.getComments');
1099
1100 if ( isset($struct['status']) )
1101 $status = $struct['status'];
1102 else
1103 $status = '';
1104
1105 $post_id = '';
1106 if ( isset($struct['post_id']) )
1107 $post_id = absint($struct['post_id']);
1108
1109 $offset = 0;
1110 if ( isset($struct['offset']) )
1111 $offset = absint($struct['offset']);
1112
1113 $number = 10;
1114 if ( isset($struct['number']) )
1115 $number = absint($struct['number']);
1116
1117 $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );
1118 $num_comments = count($comments);
1119
1120 if ( ! $num_comments )
1121 return array();
1122
1123 $comments_struct = array();
1124
1125 for ( $i = 0; $i < $num_comments; $i++ ) {
1126 $comment = wp_xmlrpc_server::wp_getComment(array(
1127 $blog_id, $username, $password, $comments[$i]->comment_ID,
1128 ));
1129 $comments_struct[] = $comment;
1130 }
1131
1132 return $comments_struct;
1133 }
1134
1135 /**
1136 * Remove comment.
1137 *
1138 * @since 2.7.0
1139 *
1140 * @param array $args Method parameters.
1141 * @return mixed {@link wp_delete_comment()}
1142 */
1143 function wp_deleteComment($args) {
1144 $this->escape($args);
1145
1146 $blog_id = (int) $args[0];
1147 $username = $args[1];
1148 $password = $args[2];
1149 $comment_ID = (int) $args[3];
1150
1151 if ( !$this->login_pass_ok( $username, $password ) )
1152 return $this->error;
1153
1154 set_current_user( 0, $username );
1155 if ( !current_user_can( 'moderate_comments' ) )
1156 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this blog.' ) );
1157
1158 do_action('xmlrpc_call', 'wp.deleteComment');
1159
1160 if ( ! get_comment($comment_ID) )
1161 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
1162
1163 return wp_delete_comment($comment_ID);
1164 }
1165
1166 /**
1167 * Edit comment.
1168 *
1169 * @since 2.7.0
1170 *
1171 * @param array $args Method parameters.
1172 * @return bool True, on success.
1173 */
1174 function wp_editComment($args) {
1175 $this->escape($args);
1176
1177 $blog_id = (int) $args[0];
1178 $username = $args[1];
1179 $password = $args[2];
1180 $comment_ID = (int) $args[3];
1181 $content_struct = $args[4];
1182
1183 if ( !$this->login_pass_ok( $username, $password ) )
1184 return $this->error;
1185
1186 set_current_user( 0, $username );
1187 if ( !current_user_can( 'moderate_comments' ) )
1188 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this blog.' ) );
1189
1190 do_action('xmlrpc_call', 'wp.editComment');
1191
1192 if ( ! get_comment($comment_ID) )
1193 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
1194
1195 if ( isset($content_struct['status']) ) {
1196 $statuses = get_comment_statuses();
1197 $statuses = array_keys($statuses);
1198
1199 if ( ! in_array($content_struct['status'], $statuses) )
1200 return new IXR_Error( 401, __( 'Invalid comment status.' ) );
1201 $comment_approved = $content_struct['status'];
1202 }
1203
1204 // Do some timestamp voodoo
1205 if ( !empty( $content_struct['date_created_gmt'] ) ) {
1206 $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
1207 $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
1208 $comment_date_gmt = iso8601_to_datetime($dateCreated, GMT);
1209 }
1210
1211 if ( isset($content_struct['content']) )
1212 $comment_content = $content_struct['content'];
1213
1214 if ( isset($content_struct['author']) )
1215 $comment_author = $content_struct['author'];
1216
1217 if ( isset($content_struct['author_url']) )
1218 $comment_author_url = $content_struct['author_url'];
1219
1220 if ( isset($content_struct['author_email']) )
1221 $comment_author_email = $content_struct['author_email'];
1222
1223 // We've got all the data -- post it:
1224 $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');
1225
1226 $result = wp_update_comment($comment);
1227 if ( is_wp_error( $result ) )
1228 return new IXR_Error(500, $result->get_error_message());
1229
1230 if ( !$result )
1231 return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));
1232
1233 return true;
1234 }
1235
1236 /**
1237 * Create new comment.
1238 *
1239 * @since 2.7.0
1240 *
1241 * @param array $args Method parameters.
1242 * @return mixed {@link wp_new_comment()}
1243 */
1244 function wp_newComment($args) {
1245 global $wpdb;
1246
1247 $this->escape($args);
1248
1249 $blog_id = (int) $args[0];
1250 $username = $args[1];
1251 $password = $args[2];
1252 $post = $args[3];
1253 $content_struct = $args[4];
1254
1255 $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);
1256
1257 if ( !$this->login_pass_ok( $username, $password ) ) {
1258 $logged_in = false;
1259 if ( $allow_anon && get_option('comment_registration') )
1260 return new IXR_Error( 403, __( 'You must be registered to comment' ) );
1261 else if ( !$allow_anon )
1262 return $this->error;
1263 } else {
1264 $logged_in = true;
1265 set_current_user( 0, $username );
1266 if ( !current_user_can( 'moderate_comments' ) )
1267 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this blog.' ) );
1268 }
1269
1270 if ( is_numeric($post) )
1271 $post_id = absint($post);
1272 else
1273 $post_id = url_to_postid($post);
1274
1275 if ( ! $post_id )
1276 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1277
1278 if ( ! get_post($post_id) )
1279 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1280
1281 $comment['comment_post_ID'] = $post_id;
1282
1283 if ( $logged_in ) {
1284 $user = wp_get_current_user();
1285 $comment['comment_author'] = $wpdb->escape( $user->display_name );
1286 $comment['comment_author_email'] = $wpdb->escape( $user->user_email );
1287 $comment['comment_author_url'] = $wpdb->escape( $user->user_url );
1288 $comment['user_ID'] = $user->ID;
1289 } else {
1290 $comment['comment_author'] = '';
1291 if ( isset($content_struct['author']) )
1292 $comment['comment_author'] = $content_struct['author'];
1293 $comment['comment_author_email'] = '';
1294 if ( isset($content_struct['author']) )
1295 $comment['comment_author_email'] = $content_struct['author_email'];
1296 $comment['comment_author_url'] = '';
1297 if ( isset($content_struct['author']) )
1298 $comment['comment_author_url'] = $content_struct['author_url'];
1299 $comment['user_ID'] = 0;
1300
1301 if ( get_option('require_name_email') ) {
1302 if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )
1303 return new IXR_Error( 403, __( 'Comment author name and email are required' ) );
1304 elseif ( !is_email($comment['comment_author_email']) )
1305 return new IXR_Error( 403, __( 'A valid email address is required' ) );
1306 }
1307 }
1308
1309 $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;
1310
1311 $comment['comment_content'] = $content_struct['content'];
1312
1313 do_action('xmlrpc_call', 'wp.newComment');
1314
1315 return wp_new_comment($comment);
1316 }
1317
1318 /**
1319 * Retrieve all of the comment status.
1320 *
1321 * @since 2.7.0
1322 *
1323 * @param array $args Method parameters.
1324 * @return array
1325 */
1326 function wp_getCommentStatusList($args) {
1327 $this->escape( $args );
1328
1329 $blog_id = (int) $args[0];
1330 $username = $args[1];
1331 $password = $args[2];
1332
1333 if ( !$this->login_pass_ok( $username, $password ) )
1334 return $this->error;
1335
1336 set_current_user( 0, $username );
1337 if ( !current_user_can( 'moderate_comments' ) )
1338 return new IXR_Error( 403, __( 'You are not allowed access to details about this blog.' ) );
1339
1340 do_action('xmlrpc_call', 'wp.getCommentStatusList');
1341
1342 return get_comment_statuses( );
1343 }
1344
1345 /**
1346 * Retrieve comment count.
1347 *
1348 * @since 2.5.0
1349 *
1350 * @param array $args Method parameters.
1351 * @return array
1352 */
1353 function wp_getCommentCount( $args ) {
1354 $this->escape($args);
1355
1356 $blog_id = (int) $args[0];
1357 $username = $args[1];
1358 $password = $args[2];
1359 $post_id = (int) $args[3];
1360
1361 if( !$this->login_pass_ok( $username, $password ) ) {
1362 return $this->error;
1363 }
1364
1365 set_current_user( 0, $username );
1366 if( !current_user_can( 'edit_posts' ) ) {
1367 return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );
1368 }
1369
1370 do_action('xmlrpc_call', 'wp.getCommentCount');
1371
1372 $count = wp_count_comments( $post_id );
1373 return array(
1374 "approved" => $count->approved,
1375 "awaiting_moderation" => $count->moderated,
1376 "spam" => $count->spam,
1377 "total_comments" => $count->total_comments
1378 );
1379 }
1380
1381 /**
1382 * Retrieve post statuses.
1383 *
1384 * @since 2.5.0
1385 *
1386 * @param array $args Method parameters.
1387 * @return array
1388 */
1389 function wp_getPostStatusList( $args ) {
1390 $this->escape( $args );
1391
1392 $blog_id = (int) $args[0];
1393 $username = $args[1];
1394 $password = $args[2];
1395
1396 if( !$this->login_pass_ok( $username, $password ) ) {
1397 return $this->error;
1398 }
1399
1400 set_current_user( 0, $username );
1401 if( !current_user_can( 'edit_posts' ) ) {
1402 return new IXR_Error( 403, __( 'You are not allowed access to details about this blog.' ) );
1403 }
1404
1405 do_action('xmlrpc_call', 'wp.getPostStatusList');
1406
1407 return get_post_statuses( );
1408 }
1409
1410 /**
1411 * Retrieve page statuses.
1412 *
1413 * @since 2.5.0
1414 *
1415 * @param array $args Method parameters.
1416 * @return array
1417 */
1418 function wp_getPageStatusList( $args ) {
1419 $this->escape( $args );
1420
1421 $blog_id = (int) $args[0];
1422 $username = $args[1];
1423 $password = $args[2];
1424
1425 if( !$this->login_pass_ok( $username, $password ) ) {
1426 return $this->error;
1427 }
1428
1429 set_current_user( 0, $username );
1430 if( !current_user_can( 'edit_posts' ) ) {
1431 return new IXR_Error( 403, __( 'You are not allowed access to details about this blog.' ) );
1432 }
1433
1434 do_action('xmlrpc_call', 'wp.getPageStatusList');
1435
1436 return get_page_statuses( );
1437 }
1438
1439 /**
1440 * Retrieve page templates.
1441 *
1442 * @since 2.6.0
1443 *
1444 * @param array $args Method parameters.
1445 * @return array
1446 */
1447 function wp_getPageTemplates( $args ) {
1448 $this->escape( $args );
1449
1450 $blog_id = (int) $args[0];
1451 $username = $args[1];
1452 $password = $args[2];
1453
1454 if( !$this->login_pass_ok( $username, $password ) ) {
1455 return $this->error;
1456 }
1457
1458 set_current_user( 0, $username );
1459 if( !current_user_can( 'edit_pages' ) ) {
1460 return new IXR_Error( 403, __( 'You are not allowed access to details about this blog.' ) );
1461 }
1462
1463 $templates = get_page_templates( );
1464 $templates['Default'] = 'default';
1465
1466 return $templates;
1467 }
1468
1469 /**
1470 * Retrieve blog options.
1471 *
1472 * @since 2.6.0
1473 *
1474 * @param array $args Method parameters.
1475 * @return array
1476 */
1477 function wp_getOptions( $args ) {
1478 $this->escape( $args );
1479
1480 $blog_id = (int) $args[0];
1481 $username = $args[1];
1482 $password = $args[2];
1483 $options = (array) $args[3];
1484
1485 if( !$this->login_pass_ok( $username, $password ) )
1486 return $this->error;
1487
1488 $user = set_current_user( 0, $username );
1489
1490 // If no specific options where asked for, return all of them
1491 if (count( $options ) == 0 ) {
1492 $options = array_keys($this->blog_options);
1493 }
1494
1495 return $this->_getOptions($options);
1496 }
1497
1498 /**
1499 * Retrieve blog options value from list.
1500 *
1501 * @since 2.6.0
1502 *
1503 * @param array $options Options to retrieve.
1504 * @return array
1505 */
1506 function _getOptions($options)
1507 {
1508 $data = array( );
1509 foreach( $options as $option ) {
1510 if( array_key_exists( $option, $this->blog_options ) )
1511 {
1512 $data[$option] = $this->blog_options[$option];
1513 //Is the value static or dynamic?
1514 if( isset( $data[$option]['option'] ) ) {
1515 $data[$option]['value'] = get_option( $data[$option]['option'] );
1516 unset($data[$option]['option']);
1517 }
1518 }
1519 }
1520
1521 return $data;
1522 }
1523
1524 /**
1525 * Update blog options.
1526 *
1527 * @since 2.6.0
1528 *
1529 * @param array $args Method parameters.
1530 * @return unknown
1531 */
1532 function wp_setOptions( $args ) {
1533 $this->escape( $args );
1534
1535 $blog_id = (int) $args[0];
1536 $username = $args[1];
1537 $password = $args[2];
1538 $options = (array) $args[3];
1539
1540 if( !$this->login_pass_ok( $username, $password ) )
1541 return $this->error;
1542
1543 $user = set_current_user( 0, $username );
1544 if( !current_user_can( 'manage_options' ) )
1545 return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );
1546
1547 foreach( $options as $o_name => $o_value ) {
1548 $option_names[] = $o_name;
1549 if( empty( $o_value ) )
1550 continue;
1551
1552 if( !array_key_exists( $o_name, $this->blog_options ) )
1553 continue;
1554
1555 if( $this->blog_options[$o_name]['readonly'] == true )
1556 continue;
1557
1558 update_option( $this->blog_options[$o_name]['option'], $o_value );
1559 }
1560
1561 //Now return the updated values
1562 return $this->_getOptions($option_names);
1563 }
1564
1565 /* Blogger API functions.
1566 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
1567 */
1568
1569 /**
1570 * Retrieve blogs that user owns.
1571 *
1572 * Will make more sense once we support multiple blogs.
1573 *
1574 * @since 1.5.0
1575 *
1576 * @param array $args Method parameters.
1577 * @return array
1578 */
1579 function blogger_getUsersBlogs($args) {
1580
1581 $this->escape($args);
1582
1583 $user_login = $args[1];
1584 $user_pass = $args[2];
1585
1586 if (!$this->login_pass_ok($user_login, $user_pass)) {
1587 return $this->error;
1588 }
1589
1590 do_action('xmlrpc_call', 'blogger.getUsersBlogs');
1591
1592 set_current_user(0, $user_login);
1593 $is_admin = current_user_can('manage_options');
1594
1595 $struct = array(
1596 'isAdmin' => $is_admin,
1597 'url' => get_option('home') . '/',
1598 'blogid' => '1',
1599 'blogName' => get_option('blogname'),
1600 'xmlrpc' => get_option('home') . '/xmlrpc.php',
1601 );
1602
1603 return array($struct);
1604 }
1605
1606 /**
1607 * Retrieve user's data.
1608 *
1609 * Gives your client some info about you, so you don't have to.
1610 *
1611 * @since 1.5.0
1612 *
1613 * @param array $args Method parameters.
1614 * @return array
1615 */
1616 function blogger_getUserInfo($args) {
1617
1618 $this->escape($args);
1619
1620 $user_login = $args[1];
1621 $user_pass = $args[2];
1622
1623 if (!$this->login_pass_ok($user_login, $user_pass)) {
1624 return $this->error;
1625 }
1626
1627 set_current_user( 0, $user_login );
1628 if( !current_user_can( 'edit_posts' ) )
1629 return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this blog.' ) );
1630
1631 do_action('xmlrpc_call', 'blogger.getUserInfo');
1632
1633 $user_data = get_userdatabylogin($user_login);
1634
1635 $struct = array(
1636 'nickname' => $user_data->nickname,
1637 'userid' => $user_data->ID,
1638 'url' => $user_data->user_url,
1639 'lastname' => $user_data->last_name,
1640 'firstname' => $user_data->first_name
1641 );
1642
1643 return $struct;
1644 }
1645
1646 /**
1647 * Retrieve post.
1648 *
1649 * @since 1.5.0
1650 *
1651 * @param array $args Method parameters.
1652 * @return array
1653 */
1654 function blogger_getPost($args) {
1655
1656 $this->escape($args);
1657
1658 $post_ID = (int) $args[1];
1659 $user_login = $args[2];
1660 $user_pass = $args[3];
1661
1662 if (!$this->login_pass_ok($user_login, $user_pass)) {
1663 return $this->error;
1664 }
1665
1666 set_current_user( 0, $user_login );
1667 if( !current_user_can( 'edit_post', $post_ID ) )
1668 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
1669
1670 do_action('xmlrpc_call', 'blogger.getPost');
1671
1672 $post_data = wp_get_single_post($post_ID, ARRAY_A);
1673
1674 $categories = implode(',', wp_get_post_categories($post_ID));
1675
1676 $content = '<title>'.stripslashes($post_data['post_title']).'</title>';
1677 $content .= '<category>'.$categories.'</category>';
1678 $content .= stripslashes($post_data['post_content']);
1679
1680 $struct = array(
1681 'userid' => $post_data['post_author'],
1682 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'])),
1683 'content' => $content,
1684 'postid' => $post_data['ID']
1685 );
1686
1687 return $struct;
1688 }
1689
1690 /**
1691 * Retrieve list of recent posts.
1692 *
1693 * @since 1.5.0
1694 *
1695 * @param array $args Method parameters.
1696 * @return array
1697 */
1698 function blogger_getRecentPosts($args) {
1699
1700 $this->escape($args);
1701
1702 $blog_ID = (int) $args[1]; /* though we don't use it yet */
1703 $user_login = $args[2];
1704 $user_pass = $args[3];
1705 $num_posts = $args[4];
1706
1707 if (!$this->login_pass_ok($user_login, $user_pass)) {
1708 return $this->error;
1709 }
1710
1711 do_action('xmlrpc_call', 'blogger.getRecentPosts');
1712
1713 $posts_list = wp_get_recent_posts($num_posts);
1714
1715 set_current_user( 0, $user_login );
1716
1717 if (!$posts_list) {
1718 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
1719 return $this->error;
1720 }
1721
1722 foreach ($posts_list as $entry) {
1723 if( !current_user_can( 'edit_post', $entry['ID'] ) )
1724 continue;
1725
1726 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']);
1727 $categories = implode(',', wp_get_post_categories($entry['ID']));
1728
1729 $content = '<title>'.stripslashes($entry['post_title']).'</title>';
1730 $content .= '<category>'.$categories.'</category>';
1731 $content .= stripslashes($entry['post_content']);
1732
1733 $struct[] = array(
1734 'userid' => $entry['post_author'],
1735 'dateCreated' => new IXR_Date($post_date),
1736 'content' => $content,
1737 'postid' => $entry['ID'],
1738 );
1739
1740 }
1741
1742 $recent_posts = array();
1743 for ($j=0; $j<count($struct); $j++) {
1744 array_push($recent_posts, $struct[$j]);
1745 }
1746
1747 return $recent_posts;
1748 }
1749
1750 /**
1751 * Retrieve blog_filename content.
1752 *
1753 * @since 1.5.0
1754 *
1755 * @param array $args Method parameters.
1756 * @return string
1757 */
1758 function blogger_getTemplate($args) {
1759
1760 $this->escape($args);
1761
1762 $blog_ID = (int) $args[1];
1763 $user_login = $args[2];
1764 $user_pass = $args[3];
1765 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
1766
1767 if (!$this->login_pass_ok($user_login, $user_pass)) {
1768 return $this->error;
1769 }
1770
1771 do_action('xmlrpc_call', 'blogger.getTemplate');
1772
1773 set_current_user(0, $user_login);
1774 if ( !current_user_can('edit_themes') ) {
1775 return new IXR_Error(401, __('Sorry, this user can not edit the template.'));
1776 }
1777
1778 /* warning: here we make the assumption that the blog's URL is on the same server */
1779 $filename = get_option('home') . '/';
1780 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
1781
1782 $f = fopen($filename, 'r');
1783 $content = fread($f, filesize($filename));
1784 fclose($f);
1785
1786 /* so it is actually editable with a windows/mac client */
1787 // FIXME: (or delete me) do we really want to cater to bad clients at the expense of good ones by BEEPing up their line breaks? commented. $content = str_replace("\n", "\r\n", $content);
1788
1789 return $content;
1790 }
1791
1792 /**
1793 * Updates the content of blog_filename.
1794 *
1795 * @since 1.5.0
1796 *
1797 * @param array $args Method parameters.
1798 * @return bool True when done.
1799 */
1800 function blogger_setTemplate($args) {
1801
1802 $this->escape($args);
1803
1804 $blog_ID = (int) $args[1];
1805 $user_login = $args[2];
1806 $user_pass = $args[3];
1807 $content = $args[4];
1808 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
1809
1810 if (!$this->login_pass_ok($user_login, $user_pass)) {
1811 return $this->error;
1812 }
1813
1814 do_action('xmlrpc_call', 'blogger.setTemplate');
1815
1816 set_current_user(0, $user_login);
1817 if ( !current_user_can('edit_themes') ) {
1818 return new IXR_Error(401, __('Sorry, this user can not edit the template.'));
1819 }
1820
1821 /* warning: here we make the assumption that the blog's URL is on the same server */
1822 $filename = get_option('home') . '/';
1823 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
1824
1825 if ($f = fopen($filename, 'w+')) {
1826 fwrite($f, $content);
1827 fclose($f);
1828 } else {
1829 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));
1830 }
1831
1832 return true;
1833 }
1834
1835 /**
1836 * Create new post.
1837 *
1838 * @since 1.5.0
1839 *
1840 * @param array $args Method parameters.
1841 * @return int
1842 */
1843 function blogger_newPost($args) {
1844
1845 $this->escape($args);
1846
1847 $blog_ID = (int) $args[1]; /* though we don't use it yet */
1848 $user_login = $args[2];
1849 $user_pass = $args[3];
1850 $content = $args[4];
1851 $publish = $args[5];
1852
1853 if (!$this->login_pass_ok($user_login, $user_pass)) {
1854 return $this->error;
1855 }
1856
1857 do_action('xmlrpc_call', 'blogger.newPost');
1858
1859 $cap = ($publish) ? 'publish_posts' : 'edit_posts';
1860 $user = set_current_user(0, $user_login);
1861 if ( !current_user_can($cap) )
1862 return new IXR_Error(401, __('Sorry, you are not allowed to post on this blog.'));
1863
1864 $post_status = ($publish) ? 'publish' : 'draft';
1865
1866 $post_author = $user->ID;
1867
1868 $post_title = xmlrpc_getposttitle($content);
1869 $post_category = xmlrpc_getpostcategory($content);
1870 $post_content = xmlrpc_removepostdata($content);
1871
1872 $post_date = current_time('mysql');
1873 $post_date_gmt = current_time('mysql', 1);
1874
1875 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
1876
1877 $post_ID = wp_insert_post($post_data);
1878 if ( is_wp_error( $post_ID ) )
1879 return new IXR_Error(500, $post_ID->get_error_message());
1880
1881 if (!$post_ID)
1882 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
1883
1884 $this->attach_uploads( $post_ID, $post_content );
1885
1886 logIO('O', "Posted ! ID: $post_ID");
1887
1888 return $post_ID;
1889 }
1890
1891 /**
1892 * Edit a post.
1893 *
1894 * @since 1.5.0
1895 *
1896 * @param array $args Method parameters.
1897 * @return bool true when done.
1898 */
1899 function blogger_editPost($args) {
1900
1901 $this->escape($args);
1902
1903 $post_ID = (int) $args[1];
1904 $user_login = $args[2];
1905 $user_pass = $args[3];
1906 $content = $args[4];
1907 $publish = $args[5];
1908
1909 if (!$this->login_pass_ok($user_login, $user_pass)) {
1910 return $this->error;
1911 }
1912
1913 do_action('xmlrpc_call', 'blogger.editPost');
1914
1915 $actual_post = wp_get_single_post($post_ID,ARRAY_A);
1916
1917 if (!$actual_post || $actual_post['post_type'] != 'post') {
1918 return new IXR_Error(404, __('Sorry, no such post.'));
1919 }
1920
1921 $this->escape($actual_post);
1922
1923 set_current_user(0, $user_login);
1924 if ( !current_user_can('edit_post', $post_ID) )
1925 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));
1926
1927 extract($actual_post, EXTR_SKIP);
1928
1929 if ( ('publish' == $post_status) && !current_user_can('publish_posts') )
1930 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
1931
1932 $post_title = xmlrpc_getposttitle($content);
1933 $post_category = xmlrpc_getpostcategory($content);
1934 $post_content = xmlrpc_removepostdata($content);
1935
1936 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
1937
1938 $result = wp_update_post($postdata);
1939
1940 if (!$result) {
1941 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));
1942 }
1943 $this->attach_uploads( $ID, $post_content );
1944
1945 return true;
1946 }
1947
1948 /**
1949 * Remove a post.
1950 *
1951 * @since 1.5.0
1952 *
1953 * @param array $args Method parameters.
1954 * @return bool True when post is deleted.
1955 */
1956 function blogger_deletePost($args) {
1957 $this->escape($args);
1958
1959 $post_ID = (int) $args[1];
1960 $user_login = $args[2];
1961 $user_pass = $args[3];
1962 $publish = $args[4];
1963
1964 if (!$this->login_pass_ok($user_login, $user_pass)) {
1965 return $this->error;
1966 }
1967
1968 do_action('xmlrpc_call', 'blogger.deletePost');
1969
1970 $actual_post = wp_get_single_post($post_ID,ARRAY_A);
1971
1972 if (!$actual_post || $actual_post['post_type'] != 'post') {
1973 return new IXR_Error(404, __('Sorry, no such post.'));
1974 }
1975
1976 set_current_user(0, $user_login);
1977 if ( !current_user_can('edit_post', $post_ID) )
1978 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));
1979
1980 $result = wp_delete_post($post_ID);
1981
1982 if (!$result) {
1983 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));
1984 }
1985
1986 return true;
1987 }
1988
1989 /* MetaWeblog API functions
1990 * specs on wherever Dave Winer wants them to be
1991 */
1992
1993 /**
1994 * Create a new post.
1995 *
1996 * @since 1.5.0
1997 *
1998 * @param array $args Method parameters.
1999 * @return int
2000 */
2001 function mw_newPost($args) {
2002 $this->escape($args);
2003
2004 $blog_ID = (int) $args[0]; // we will support this in the near future
2005 $user_login = $args[1];
2006 $user_pass = $args[2];
2007 $content_struct = $args[3];
2008 $publish = $args[4];
2009
2010 if (!$this->login_pass_ok($user_login, $user_pass)) {
2011 return $this->error;
2012 }
2013 $user = set_current_user(0, $user_login);
2014
2015 do_action('xmlrpc_call', 'metaWeblog.newPost');
2016
2017 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
2018 $error_message = __( 'Sorry, you are not allowed to publish posts on this blog.' );
2019 $post_type = 'post';
2020 $page_template = '';
2021 if( !empty( $content_struct['post_type'] ) ) {
2022 if( $content_struct['post_type'] == 'page' ) {
2023 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';
2024 $error_message = __( 'Sorry, you are not allowed to publish pages on this blog.' );
2025 $post_type = 'page';
2026 if( !empty( $content_struct['wp_page_template'] ) )
2027 $page_template = $content_struct['wp_page_template'];
2028 }
2029 elseif( $content_struct['post_type'] == 'post' ) {
2030 // This is the default, no changes needed
2031 }
2032 else {
2033 // No other post_type values are allowed here
2034 return new IXR_Error( 401, __( 'Invalid post type.' ) );
2035 }
2036 }
2037
2038 if( !current_user_can( $cap ) ) {
2039 return new IXR_Error( 401, $error_message );
2040 }
2041
2042 // Let WordPress generate the post_name (slug) unless
2043 // one has been provided.
2044 $post_name = "";
2045 if(isset($content_struct["wp_slug"])) {
2046 $post_name = $content_struct["wp_slug"];
2047 }
2048
2049 // Only use a password if one was given.
2050 if(isset($content_struct["wp_password"])) {
2051 $post_password = $content_struct["wp_password"];
2052 }
2053
2054 // Only set a post parent if one was provided.
2055 if(isset($content_struct["wp_page_parent_id"])) {
2056 $post_parent = $content_struct["wp_page_parent_id"];
2057 }
2058
2059 // Only set the menu_order if it was provided.
2060 if(isset($content_struct["wp_page_order"])) {
2061 $menu_order = $content_struct["wp_page_order"];
2062 }
2063
2064 $post_author = $user->ID;
2065
2066 // If an author id was provided then use it instead.
2067 if(
2068 isset($content_struct["wp_author_id"])
2069 && ($user->ID != $content_struct["wp_author_id"])
2070 ) {
2071 switch($post_type) {
2072 case "post":
2073 if(!current_user_can("edit_others_posts")) {
2074 return(new IXR_Error(401, __("You are not allowed to post as this user")));
2075 }
2076 break;
2077 case "page":
2078 if(!current_user_can("edit_others_pages")) {
2079 return(new IXR_Error(401, __("You are not allowed to create pages as this user")));
2080 }
2081 break;
2082 default:
2083 return(new IXR_Error(401, __("Invalid post type.")));
2084 break;
2085 }
2086 $post_author = $content_struct["wp_author_id"];
2087 }
2088
2089 $post_title = $content_struct['title'];
2090 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] );
2091
2092 $post_status = $publish ? 'publish' : 'draft';
2093
2094 if( isset( $content_struct["{$post_type}_status"] ) ) {
2095 switch( $content_struct["{$post_type}_status"] ) {
2096 case 'draft':
2097 case 'private':
2098 case 'publish':
2099 $post_status = $content_struct["{$post_type}_status"];
2100 break;
2101 case 'pending':
2102 // Pending is only valid for posts, not pages.
2103 if( $post_type === 'post' ) {
2104 $post_status = $content_struct["{$post_type}_status"];
2105 }
2106 break;
2107 default:
2108 $post_status = $publish ? 'publish' : 'draft';
2109 break;
2110 }
2111 }
2112
2113 $post_excerpt = $content_struct['mt_excerpt'];
2114 $post_more = $content_struct['mt_text_more'];
2115
2116 $tags_input = $content_struct['mt_keywords'];
2117
2118 if(isset($content_struct["mt_allow_comments"])) {
2119 if(!is_numeric($content_struct["mt_allow_comments"])) {
2120 switch($content_struct["mt_allow_comments"]) {
2121 case "closed":
2122 $comment_status = "closed";
2123 break;
2124 case "open":
2125 $comment_status = "open";
2126 break;
2127 default:
2128 $comment_status = get_option("default_comment_status");
2129 break;
2130 }
2131 }
2132 else {
2133 switch((int) $content_struct["mt_allow_comments"]) {
2134 case 0:
2135 case 2:
2136 $comment_status = "closed";
2137 break;
2138 case 1:
2139 $comment_status = "open";
2140 break;
2141 default:
2142 $comment_status = get_option("default_comment_status");
2143 break;
2144 }
2145 }
2146 }
2147 else {
2148 $comment_status = get_option("default_comment_status");
2149 }
2150
2151 if(isset($content_struct["mt_allow_pings"])) {
2152 if(!is_numeric($content_struct["mt_allow_pings"])) {
2153 switch($content_struct['mt_allow_pings']) {
2154 case "closed":
2155 $ping_status = "closed";
2156 break;
2157 case "open":
2158 $ping_status = "open";
2159 break;
2160 default:
2161 $ping_status = get_option("default_ping_status");
2162 break;
2163 }
2164 }
2165 else {
2166 switch((int) $content_struct["mt_allow_pings"]) {
2167 case 0:
2168 $ping_status = "closed";
2169 break;
2170 case 1:
2171 $ping_status = "open";
2172 break;
2173 default:
2174 $ping_status = get_option("default_ping_status");
2175 break;
2176 }
2177 }
2178 }
2179 else {
2180 $ping_status = get_option("default_ping_status");
2181 }
2182
2183 if ($post_more) {
2184 $post_content = $post_content . "<!--more-->" . $post_more;
2185 }
2186
2187 $to_ping = $content_struct['mt_tb_ping_urls'];
2188 if ( is_array($to_ping) )
2189 $to_ping = implode(' ', $to_ping);
2190
2191 // Do some timestamp voodoo
2192 if ( !empty( $content_struct['date_created_gmt'] ) )
2193 $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
2194 elseif ( !empty( $content_struct['dateCreated']) )
2195 $dateCreated = $content_struct['dateCreated']->getIso();
2196
2197 if ( !empty( $dateCreated ) ) {
2198 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
2199 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);
2200 } else {
2201 $post_date = current_time('mysql');
2202 $post_date_gmt = current_time('mysql', 1);
2203 }
2204
2205 $catnames = $content_struct['categories'];
2206 logIO('O', 'Post cats: ' . var_export($catnames,true));
2207 $post_category = array();
2208
2209 if (is_array($catnames)) {
2210 foreach ($catnames as $cat) {
2211 $post_category[] = get_cat_ID($cat);
2212 }
2213 }
2214
2215 // We've got all the data -- post it:
2216 $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template');
2217
2218 $post_ID = wp_insert_post($postdata, true);
2219 if ( is_wp_error( $post_ID ) )
2220 return new IXR_Error(500, $post_ID->get_error_message());
2221
2222 if (!$post_ID) {
2223 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
2224 }
2225
2226 if ( isset($content_struct['custom_fields']) ) {
2227 $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
2228 }
2229
2230 // Handle enclosures
2231 $enclosure = $content_struct['enclosure'];
2232 if( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
2233 add_post_meta( $post_ID, 'enclosure', $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] );
2234 }
2235
2236 $this->attach_uploads( $post_ID, $post_content );
2237
2238 logIO('O', "Posted ! ID: $post_ID");
2239
2240 return strval($post_ID);
2241 }
2242
2243 /**
2244 * Attach upload to a post.
2245 *
2246 * @since 2.1.0
2247 *
2248 * @param int $post_ID Post ID.
2249 * @param string $post_content Post Content for attachment.
2250 */
2251 function attach_uploads( $post_ID, $post_content ) {
2252 global $wpdb;
2253
2254 // find any unattached files
2255 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '-1' AND post_type = 'attachment'" );
2256 if( is_array( $attachments ) ) {
2257 foreach( $attachments as $file ) {
2258 if( strpos( $post_content, $file->guid ) !== false ) {
2259 $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_parent = %d WHERE ID = %d", $post_ID, $file->ID) );
2260 }
2261 }
2262 }
2263 }
2264
2265 /**
2266 * Edit a post.
2267 *
2268 * @since 1.5.0
2269 *
2270 * @param array $args Method parameters.
2271 * @return bool True on success.
2272 */
2273 function mw_editPost($args) {
2274
2275 $this->escape($args);
2276
2277 $post_ID = (int) $args[0];
2278 $user_login = $args[1];
2279 $user_pass = $args[2];
2280 $content_struct = $args[3];
2281 $publish = $args[4];
2282
2283 if (!$this->login_pass_ok($user_login, $user_pass)) {
2284 return $this->error;
2285 }
2286 $user = set_current_user(0, $user_login);
2287
2288 do_action('xmlrpc_call', 'metaWeblog.editPost');
2289
2290 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
2291 $error_message = __( 'Sorry, you are not allowed to publish posts on this blog.' );
2292 $post_type = 'post';
2293 $page_template = '';
2294 if( !empty( $content_struct['post_type'] ) ) {
2295 if( $content_struct['post_type'] == 'page' ) {
2296 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';
2297 $error_message = __( 'Sorry, you are not allowed to publish pages on this blog.' );
2298 $post_type = 'page';
2299 if( !empty( $content_struct['wp_page_template'] ) )
2300 $page_template = $content_struct['wp_page_template'];
2301 }
2302 elseif( $content_struct['post_type'] == 'post' ) {
2303 // This is the default, no changes needed
2304 }
2305 else {
2306 // No other post_type values are allowed here
2307 return new IXR_Error( 401, __( 'Invalid post type.' ) );
2308 }
2309 }
2310
2311 if( !current_user_can( $cap ) ) {
2312 return new IXR_Error( 401, $error_message );
2313 }
2314
2315 $postdata = wp_get_single_post($post_ID, ARRAY_A);
2316
2317 // If there is no post data for the give post id, stop
2318 // now and return an error. Other wise a new post will be
2319 // created (which was the old behavior).
2320 if(empty($postdata["ID"])) {
2321 return(new IXR_Error(404, __("Invalid post id.")));
2322 }
2323
2324 $this->escape($postdata);
2325 extract($postdata, EXTR_SKIP);
2326
2327 // Let WordPress manage slug if none was provided.
2328 $post_name = "";
2329 if(isset($content_struct["wp_slug"])) {
2330 $post_name = $content_struct["wp_slug"];
2331 }
2332
2333 // Only use a password if one was given.
2334 if(isset($content_struct["wp_password"])) {
2335 $post_password = $content_struct["wp_password"];
2336 }
2337
2338 // Only set a post parent if one was given.
2339 if(isset($content_struct["wp_page_parent_id"])) {
2340 $post_parent = $content_struct["wp_page_parent_id"];
2341 }
2342
2343 // Only set the menu_order if it was given.
2344 if(isset($content_struct["wp_page_order"])) {
2345 $menu_order = $content_struct["wp_page_order"];
2346 }
2347
2348 $post_author = $postdata["post_author"];
2349
2350 // Only set the post_author if one is set.
2351 if(
2352 isset($content_struct["wp_author_id"])
2353 && ($user->ID != $content_struct["wp_author_id"])
2354 ) {
2355 switch($post_type) {
2356 case "post":
2357 if(!current_user_can("edit_others_posts")) {
2358 return(new IXR_Error(401, __("You are not allowed to change the post author as this user.")));
2359 }
2360 break;
2361 case "page":
2362 if(!current_user_can("edit_others_pages")) {
2363 return(new IXR_Error(401, __("You are not allowed to change the page author as this user.")));
2364 }
2365 break;
2366 default:
2367 return(new IXR_Error(401, __("Invalid post type.")));
2368 break;
2369 }
2370 $post_author = $content_struct["wp_author_id"];
2371 }
2372
2373 if(isset($content_struct["mt_allow_comments"])) {
2374 if(!is_numeric($content_struct["mt_allow_comments"])) {
2375 switch($content_struct["mt_allow_comments"]) {
2376 case "closed":
2377 $comment_status = "closed";
2378 break;
2379 case "open":
2380 $comment_status = "open";
2381 break;
2382 default:
2383 $comment_status = get_option("default_comment_status");
2384 break;
2385 }
2386 }
2387 else {
2388 switch((int) $content_struct["mt_allow_comments"]) {
2389 case 0:
2390 case 2:
2391 $comment_status = "closed";
2392 break;
2393 case 1:
2394 $comment_status = "open";
2395 break;
2396 default:
2397 $comment_status = get_option("default_comment_status");
2398 break;
2399 }
2400 }
2401 }
2402
2403 if(isset($content_struct["mt_allow_pings"])) {
2404 if(!is_numeric($content_struct["mt_allow_pings"])) {
2405 switch($content_struct["mt_allow_pings"]) {
2406 case "closed":
2407 $ping_status = "closed";
2408 break;
2409 case "open":
2410 $ping_status = "open";
2411 break;
2412 default:
2413 $ping_status = get_option("default_ping_status");
2414 break;
2415 }
2416 }
2417 else {
2418 switch((int) $content_struct["mt_allow_pings"]) {
2419 case 0:
2420 $ping_status = "closed";
2421 break;
2422 case 1:
2423 $ping_status = "open";
2424 break;
2425 default:
2426 $ping_status = get_option("default_ping_status");
2427 break;
2428 }
2429 }
2430 }
2431
2432 $post_title = $content_struct['title'];
2433 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] );
2434 $catnames = $content_struct['categories'];
2435
2436 $post_category = array();
2437
2438 if (is_array($catnames)) {
2439 foreach ($catnames as $cat) {
2440 $post_category[] = get_cat_ID($cat);
2441 }
2442 }
2443
2444 $post_excerpt = $content_struct['mt_excerpt'];
2445 $post_more = $content_struct['mt_text_more'];
2446
2447 $post_status = $publish ? 'publish' : 'draft';
2448 if( isset( $content_struct["{$post_type}_status"] ) ) {
2449 switch( $content_struct["{$post_type}_status"] ) {
2450 case 'draft':
2451 case 'private':
2452 case 'publish':
2453 $post_status = $content_struct["{$post_type}_status"];
2454 break;
2455 case 'pending':
2456 // Pending is only valid for posts, not pages.
2457 if( $post_type === 'post' ) {
2458 $post_status = $content_struct["{$post_type}_status"];
2459 }
2460 break;
2461 default:
2462 $post_status = $publish ? 'publish' : 'draft';
2463 break;
2464 }
2465 }
2466
2467 $tags_input = $content_struct['mt_keywords'];
2468
2469 if ( ('publish' == $post_status) ) {
2470 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )
2471 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));
2472 else if ( !current_user_can('publish_posts') )
2473 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
2474 }
2475
2476 if ($post_more) {
2477 $post_content = $post_content . "<!--more-->" . $post_more;
2478 }
2479
2480 $to_ping = $content_struct['mt_tb_ping_urls'];
2481 if ( is_array($to_ping) )
2482 $to_ping = implode(' ', $to_ping);
2483
2484 // Do some timestamp voodoo
2485 if ( !empty( $content_struct['date_created_gmt'] ) )
2486 $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
2487 elseif ( !empty( $content_struct['dateCreated']) )
2488 $dateCreated = $content_struct['dateCreated']->getIso();
2489
2490 if ( !empty( $dateCreated ) ) {
2491 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
2492 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);
2493 } else {
2494 $post_date = $postdata['post_date'];
2495 $post_date_gmt = $postdata['post_date_gmt'];
2496 }
2497
2498 // We've got all the data -- post it:
2499 $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template');
2500
2501 $result = wp_update_post($newpost, true);
2502 if ( is_wp_error( $result ) )
2503 return new IXR_Error(500, $result->get_error_message());
2504
2505 if (!$result) {
2506 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));
2507 }
2508
2509 if ( isset($content_struct['custom_fields']) ) {
2510 $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
2511 }
2512
2513 // Handle enclosures
2514 $enclosure = $content_struct['enclosure'];
2515 if( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
2516 add_post_meta( $post_ID, 'enclosure', $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] );
2517 }
2518
2519 $this->attach_uploads( $ID, $post_content );
2520
2521 logIO('O',"(MW) Edited ! ID: $post_ID");
2522
2523 return true;
2524 }
2525
2526 /**
2527 * Retrieve post.
2528 *
2529 * @since 1.5.0
2530 *
2531 * @param array $args Method parameters.
2532 * @return array
2533 */
2534 function mw_getPost($args) {
2535
2536 $this->escape($args);
2537
2538 $post_ID = (int) $args[0];
2539 $user_login = $args[1];
2540 $user_pass = $args[2];
2541
2542 if (!$this->login_pass_ok($user_login, $user_pass)) {
2543 return $this->error;
2544 }
2545
2546 set_current_user( 0, $user_login );
2547 if( !current_user_can( 'edit_post', $post_ID ) )
2548 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
2549
2550 do_action('xmlrpc_call', 'metaWeblog.getPost');
2551
2552 $postdata = wp_get_single_post($post_ID, ARRAY_A);
2553
2554 if ($postdata['post_date'] != '') {
2555 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date']);
2556 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt']);
2557
2558 $categories = array();
2559 $catids = wp_get_post_categories($post_ID);
2560 foreach($catids as $catid)
2561 $categories[] = get_cat_name($catid);
2562
2563 $tagnames = array();
2564 $tags = wp_get_post_tags( $post_ID );
2565 if ( !empty( $tags ) ) {
2566 foreach ( $tags as $tag )
2567 $tagnames[] = $tag->name;
2568 $tagnames = implode( ', ', $tagnames );
2569 } else {
2570 $tagnames = '';
2571 }
2572
2573 $post = get_extended($postdata['post_content']);
2574 $link = post_permalink($postdata['ID']);
2575
2576 // Get the author info.
2577 $author = get_userdata($postdata['post_author']);
2578
2579 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;
2580 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;
2581
2582 // Consider future posts as published
2583 if( $postdata['post_status'] === 'future' ) {
2584 $postdata['post_status'] = 'publish';
2585 }
2586
2587 $enclosure = array();
2588 foreach ( (array) get_post_custom($post_ID) as $key => $val) {
2589 if ($key == 'enclosure') {
2590 foreach ( (array) $val as $enc ) {
2591 $encdata = split("\n", $enc);
2592 $enclosure['url'] = trim(htmlspecialchars($encdata[0]));
2593 $enclosure['length'] = trim($encdata[1]);
2594 $enclosure['type'] = trim($encdata[2]);
2595 break 2;
2596 }
2597 }
2598 }
2599
2600 $resp = array(
2601 'dateCreated' => new IXR_Date($post_date),
2602 'userid' => $postdata['post_author'],
2603 'postid' => $postdata['ID'],
2604 'description' => $post['main'],
2605 'title' => $postdata['post_title'],
2606 'link' => $link,
2607 'permaLink' => $link,
2608 // commented out because no other tool seems to use this
2609 // 'content' => $entry['post_content'],
2610 'categories' => $categories,
2611 'mt_excerpt' => $postdata['post_excerpt'],
2612 'mt_text_more' => $post['extended'],
2613 'mt_allow_comments' => $allow_comments,
2614 'mt_allow_pings' => $allow_pings,
2615 'mt_keywords' => $tagnames,
2616 'wp_slug' => $postdata['post_name'],
2617 'wp_password' => $postdata['post_password'],
2618 'wp_author_id' => $author->ID,
2619 'wp_author_display_name' => $author->display_name,
2620 'date_created_gmt' => new IXR_Date($post_date_gmt),
2621 'post_status' => $postdata['post_status'],
2622 'custom_fields' => $this->get_custom_fields($post_ID)
2623 );
2624
2625 if (!empty($enclosure)) $resp['enclosure'] = $enclosure;
2626
2627 return $resp;
2628 } else {
2629 return new IXR_Error(404, __('Sorry, no such post.'));
2630 }
2631 }
2632
2633 /**
2634 * Retrieve list of recent posts.
2635 *
2636 * @since 1.5.0
2637 *
2638 * @param array $args Method parameters.
2639 * @return array
2640 */
2641 function mw_getRecentPosts($args) {
2642
2643 $this->escape($args);
2644
2645 $blog_ID = (int) $args[0];
2646 $user_login = $args[1];
2647 $user_pass = $args[2];
2648 $num_posts = (int) $args[3];
2649
2650 if (!$this->login_pass_ok($user_login, $user_pass)) {
2651 return $this->error;
2652 }
2653
2654 do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');
2655
2656 $posts_list = wp_get_recent_posts($num_posts);
2657
2658 if (!$posts_list) {
2659 return array( );
2660 }
2661
2662 set_current_user( 0, $user_login );
2663
2664 foreach ($posts_list as $entry) {
2665 if( !current_user_can( 'edit_post', $entry['ID'] ) )
2666 continue;
2667
2668 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']);
2669 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt']);
2670
2671 $categories = array();
2672 $catids = wp_get_post_categories($entry['ID']);
2673 foreach($catids as $catid) {
2674 $categories[] = get_cat_name($catid);
2675 }
2676
2677 $tagnames = array();
2678 $tags = wp_get_post_tags( $entry['ID'] );
2679 if ( !empty( $tags ) ) {
2680 foreach ( $tags as $tag ) {
2681 $tagnames[] = $tag->name;
2682 }
2683 $tagnames = implode( ', ', $tagnames );
2684 } else {
2685 $tagnames = '';
2686 }
2687
2688 $post = get_extended($entry['post_content']);
2689 $link = post_permalink($entry['ID']);
2690
2691 // Get the post author info.
2692 $author = get_userdata($entry['post_author']);
2693
2694 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;
2695 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;
2696
2697 // Consider future posts as published
2698 if( $entry['post_status'] === 'future' ) {
2699 $entry['post_status'] = 'publish';
2700 }
2701
2702 $struct[] = array(
2703 'dateCreated' => new IXR_Date($post_date),
2704 'userid' => $entry['post_author'],
2705 'postid' => $entry['ID'],
2706 'description' => $post['main'],
2707 'title' => $entry['post_title'],
2708 'link' => $link,
2709 'permaLink' => $link,
2710 // commented out because no other tool seems to use this
2711 // 'content' => $entry['post_content'],
2712 'categories' => $categories,
2713 'mt_excerpt' => $entry['post_excerpt'],
2714 'mt_text_more' => $post['extended'],
2715 'mt_allow_comments' => $allow_comments,
2716 'mt_allow_pings' => $allow_pings,
2717 'mt_keywords' => $tagnames,
2718 'wp_slug' => $entry['post_name'],
2719 'wp_password' => $entry['post_password'],
2720 'wp_author_id' => $author->ID,
2721 'wp_author_display_name' => $author->display_name,
2722 'date_created_gmt' => new IXR_Date($post_date_gmt),
2723 'post_status' => $entry['post_status'],
2724 'custom_fields' => $this->get_custom_fields($entry['ID'])
2725 );
2726
2727 }
2728
2729 $recent_posts = array();
2730 for ($j=0; $j<count($struct); $j++) {
2731 array_push($recent_posts, $struct[$j]);
2732 }
2733
2734 return $recent_posts;
2735 }
2736
2737 /**
2738 * Retrieve the list of categories on a given blog.
2739 *
2740 * @since 1.5.0
2741 *
2742 * @param array $args Method parameters.
2743 * @return array
2744 */
2745 function mw_getCategories($args) {
2746
2747 $this->escape($args);
2748
2749 $blog_ID = (int) $args[0];
2750 $user_login = $args[1];
2751 $user_pass = $args[2];
2752
2753 if (!$this->login_pass_ok($user_login, $user_pass)) {
2754 return $this->error;
2755 }
2756
2757 set_current_user( 0, $user_login );
2758 if( !current_user_can( 'edit_posts' ) )
2759 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this blog in order to view categories.' ) );
2760
2761 do_action('xmlrpc_call', 'metaWeblog.getCategories');
2762
2763 $categories_struct = array();
2764
2765 if ( $cats = get_categories('get=all') ) {
2766 foreach ( $cats as $cat ) {
2767 $struct['categoryId'] = $cat->term_id;
2768 $struct['parentId'] = $cat->parent;
2769 $struct['description'] = $cat->description;
2770 $struct['categoryName'] = $cat->name;
2771 $struct['htmlUrl'] = wp_specialchars(get_category_link($cat->term_id));
2772 $struct['rssUrl'] = wp_specialchars(get_category_feed_link($cat->term_id, 'rss2'));
2773
2774 $categories_struct[] = $struct;
2775 }
2776 }
2777
2778 return $categories_struct;
2779 }
2780
2781 /**
2782 * Uploads a file, following your settings.
2783 *
2784 * Adapted from a patch by Johann Richard.
2785 *
2786 * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
2787 *
2788 * @since 1.5.0
2789 *
2790 * @param array $args Method parameters.
2791 * @return array
2792 */
2793 function mw_newMediaObject($args) {
2794 global $wpdb;
2795
2796 $blog_ID = (int) $args[0];
2797 $user_login = $wpdb->escape($args[1]);
2798 $user_pass = $wpdb->escape($args[2]);
2799 $data = $args[3];
2800
2801 $name = sanitize_file_name( $data['name'] );
2802 $type = $data['type'];
2803 $bits = $data['bits'];
2804
2805 logIO('O', '(MW) Received '.strlen($bits).' bytes');
2806
2807 if ( !$this->login_pass_ok($user_login, $user_pass) )
2808 return $this->error;
2809
2810 do_action('xmlrpc_call', 'metaWeblog.newMediaObject');
2811
2812 set_current_user(0, $user_login);
2813 if ( !current_user_can('upload_files') ) {
2814 logIO('O', '(MW) User does not have upload_files capability');
2815 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));
2816 return $this->error;
2817 }
2818
2819 if ( $upload_err = apply_filters( "pre_upload_error", false ) )
2820 return new IXR_Error(500, $upload_err);
2821
2822 if(!empty($data["overwrite"]) && ($data["overwrite"] == true)) {
2823 // Get postmeta info on the object.
2824 $old_file = $wpdb->get_row("
2825 SELECT ID
2826 FROM {$wpdb->posts}
2827 WHERE post_title = '{$name}'
2828 AND post_type = 'attachment'
2829 ");
2830
2831 // Delete previous file.
2832 wp_delete_attachment($old_file->ID);
2833
2834 // Make sure the new name is different by pre-pending the
2835 // previous post id.
2836 $filename = preg_replace("/^wpid\d+-/", "", $name);
2837 $name = "wpid{$old_file->ID}-{$filename}";
2838 }
2839
2840 $upload = wp_upload_bits($name, $type, $bits);
2841 if ( ! empty($upload['error']) ) {
2842 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
2843 logIO('O', '(MW) ' . $errorString);
2844 return new IXR_Error(500, $errorString);
2845 }
2846 // Construct the attachment array
2847 // attach to post_id -1
2848 $post_id = -1;
2849 $attachment = array(
2850 'post_title' => $name,
2851 'post_content' => '',
2852 'post_type' => 'attachment',
2853 'post_parent' => $post_id,
2854 'post_mime_type' => $type,
2855 'guid' => $upload[ 'url' ]
2856 );
2857
2858 // Save the data
2859 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
2860 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
2861
2862 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ) );
2863 }
2864
2865 /* MovableType API functions
2866 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html
2867 */
2868
2869 /**
2870 * Retrieve the post titles of recent posts.
2871 *
2872 * @since 1.5.0
2873 *
2874 * @param array $args Method parameters.
2875 * @return array
2876 */
2877 function mt_getRecentPostTitles($args) {
2878
2879 $this->escape($args);
2880
2881 $blog_ID = (int) $args[0];
2882 $user_login = $args[1];
2883 $user_pass = $args[2];
2884 $num_posts = (int) $args[3];
2885
2886 if (!$this->login_pass_ok($user_login, $user_pass)) {
2887 return $this->error;
2888 }
2889
2890 do_action('xmlrpc_call', 'mt.getRecentPostTitles');
2891
2892 $posts_list = wp_get_recent_posts($num_posts);
2893
2894 if (!$posts_list) {
2895 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
2896 return $this->error;
2897 }
2898
2899 set_current_user( 0, $user_login );
2900
2901 foreach ($posts_list as $entry) {
2902 if( !current_user_can( 'edit_post', $entry['ID'] ) )
2903 continue;
2904
2905 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']);
2906 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt']);
2907
2908 $struct[] = array(
2909 'dateCreated' => new IXR_Date($post_date),
2910 'userid' => $entry['post_author'],
2911 'postid' => $entry['ID'],
2912 'title' => $entry['post_title'],
2913 'date_created_gmt' => new IXR_Date($post_date_gmt)
2914 );
2915
2916 }
2917
2918 $recent_posts = array();
2919 for ($j=0; $j<count($struct); $j++) {
2920 array_push($recent_posts, $struct[$j]);
2921 }
2922
2923 return $recent_posts;
2924 }
2925
2926 /**
2927 * Retrieve list of all categories on blog.
2928 *
2929 * @since 1.5.0
2930 *
2931 * @param array $args Method parameters.
2932 * @return array
2933 */
2934 function mt_getCategoryList($args) {
2935
2936 $this->escape($args);
2937
2938 $blog_ID = (int) $args[0];
2939 $user_login = $args[1];
2940 $user_pass = $args[2];
2941
2942 if (!$this->login_pass_ok($user_login, $user_pass)) {
2943 return $this->error;
2944 }
2945
2946 set_current_user( 0, $user_login );
2947 if( !current_user_can( 'edit_posts' ) )
2948 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this blog in order to view categories.' ) );
2949
2950 do_action('xmlrpc_call', 'mt.getCategoryList');
2951
2952 $categories_struct = array();
2953
2954 if ( $cats = get_categories('hide_empty=0&hierarchical=0') ) {
2955 foreach ($cats as $cat) {
2956 $struct['categoryId'] = $cat->term_id;
2957 $struct['categoryName'] = $cat->name;
2958
2959 $categories_struct[] = $struct;
2960 }
2961 }
2962
2963 return $categories_struct;
2964 }
2965
2966 /**
2967 * Retrieve post categories.
2968 *
2969 * @since 1.5.0
2970 *
2971 * @param array $args Method parameters.
2972 * @return array
2973 */
2974 function mt_getPostCategories($args) {
2975
2976 $this->escape($args);
2977
2978 $post_ID = (int) $args[0];
2979 $user_login = $args[1];
2980 $user_pass = $args[2];
2981
2982 if (!$this->login_pass_ok($user_login, $user_pass)) {
2983 return $this->error;
2984 }
2985
2986 set_current_user( 0, $user_login );
2987 if( !current_user_can( 'edit_post', $post_ID ) )
2988 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
2989
2990 do_action('xmlrpc_call', 'mt.getPostCategories');
2991
2992 $categories = array();
2993 $catids = wp_get_post_categories(intval($post_ID));
2994 // first listed category will be the primary category
2995 $isPrimary = true;
2996 foreach($catids as $catid) {
2997 $categories[] = array(
2998 'categoryName' => get_cat_name($catid),
2999 'categoryId' => (string) $catid,
3000 'isPrimary' => $isPrimary
3001 );
3002 $isPrimary = false;
3003 }
3004
3005 return $categories;
3006 }
3007
3008 /**
3009 * Sets categories for a post.
3010 *
3011 * @since 1.5.0
3012 *
3013 * @param array $args Method parameters.
3014 * @return bool True on success.
3015 */
3016 function mt_setPostCategories($args) {
3017
3018 $this->escape($args);
3019
3020 $post_ID = (int) $args[0];
3021 $user_login = $args[1];
3022 $user_pass = $args[2];
3023 $categories = $args[3];
3024
3025 if (!$this->login_pass_ok($user_login, $user_pass)) {
3026 return $this->error;
3027 }
3028
3029 do_action('xmlrpc_call', 'mt.setPostCategories');
3030
3031 set_current_user(0, $user_login);
3032 if ( !current_user_can('edit_post', $post_ID) )
3033 return new IXR_Error(401, __('Sorry, you can not edit this post.'));
3034
3035 foreach($categories as $cat) {
3036 $catids[] = $cat['categoryId'];
3037 }
3038
3039 wp_set_post_categories($post_ID, $catids);
3040
3041 return true;
3042 }
3043
3044 /**
3045 * Retrieve an array of methods supported by this server.
3046 *
3047 * @since 1.5.0
3048 *
3049 * @param array $args Method parameters.
3050 * @return array
3051 */
3052 function mt_supportedMethods($args) {
3053
3054 do_action('xmlrpc_call', 'mt.supportedMethods');
3055
3056 $supported_methods = array();
3057 foreach($this->methods as $key=>$value) {
3058 $supported_methods[] = $key;
3059 }
3060
3061 return $supported_methods;
3062 }
3063
3064 /**
3065 * Retrieve an empty array because we don't support per-post text filters.
3066 *
3067 * @since 1.5.0
3068 *
3069 * @param array $args Method parameters.
3070 */
3071 function mt_supportedTextFilters($args) {
3072 do_action('xmlrpc_call', 'mt.supportedTextFilters');
3073 return apply_filters('xmlrpc_text_filters', array());
3074 }
3075
3076 /**
3077 * Retrieve trackbacks sent to a given post.
3078 *
3079 * @since 1.5.0
3080 *
3081 * @param array $args Method parameters.
3082 * @return mixed
3083 */
3084 function mt_getTrackbackPings($args) {
3085
3086 global $wpdb;
3087
3088 $post_ID = intval($args);
3089
3090 do_action('xmlrpc_call', 'mt.getTrackbackPings');
3091
3092 $actual_post = wp_get_single_post($post_ID, ARRAY_A);
3093
3094 if (!$actual_post) {
3095 return new IXR_Error(404, __('Sorry, no such post.'));
3096 }
3097
3098 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
3099
3100 if (!$comments) {
3101 return array();
3102 }
3103
3104 $trackback_pings = array();
3105 foreach($comments as $comment) {
3106 if ( 'trackback' == $comment->comment_type ) {
3107 $content = $comment->comment_content;
3108 $title = substr($content, 8, (strpos($content, '</strong>') - 8));
3109 $trackback_pings[] = array(
3110 'pingTitle' => $title,
3111 'pingURL' => $comment->comment_author_url,
3112 'pingIP' => $comment->comment_author_IP
3113 );
3114 }
3115 }
3116
3117 return $trackback_pings;
3118 }
3119
3120 /**
3121 * Sets a post's publish status to 'publish'.
3122 *
3123 * @since 1.5.0
3124 *
3125 * @param array $args Method parameters.
3126 * @return int
3127 */
3128 function mt_publishPost($args) {
3129
3130 $this->escape($args);
3131
3132 $post_ID = (int) $args[0];
3133 $user_login = $args[1];
3134 $user_pass = $args[2];
3135
3136 if (!$this->login_pass_ok($user_login, $user_pass)) {
3137 return $this->error;
3138 }
3139
3140 do_action('xmlrpc_call', 'mt.publishPost');
3141
3142 set_current_user(0, $user_login);
3143 if ( !current_user_can('edit_post', $post_ID) )
3144 return new IXR_Error(401, __('Sorry, you can not edit this post.'));
3145
3146 $postdata = wp_get_single_post($post_ID,ARRAY_A);
3147
3148 $postdata['post_status'] = 'publish';
3149
3150 // retain old cats
3151 $cats = wp_get_post_categories($post_ID);
3152 $postdata['post_category'] = $cats;
3153 $this->escape($postdata);
3154
3155 $result = wp_update_post($postdata);
3156
3157 return $result;
3158 }
3159
3160 /* PingBack functions
3161 * specs on www.hixie.ch/specs/pingback/pingback
3162 */
3163
3164 /**
3165 * Retrieves a pingback and registers it.
3166 *
3167 * @since 1.5.0
3168 *
3169 * @param array $args Method parameters.
3170 * @return array
3171 */
3172 function pingback_ping($args) {
3173 global $wpdb;
3174
3175 do_action('xmlrpc_call', 'pingback.ping');
3176
3177 $this->escape($args);
3178
3179 $pagelinkedfrom = $args[0];
3180 $pagelinkedto = $args[1];
3181
3182 $title = '';
3183
3184 $pagelinkedfrom = str_replace('&amp;', '&', $pagelinkedfrom);
3185 $pagelinkedto = str_replace('&amp;', '&', $pagelinkedto);
3186 $pagelinkedto = str_replace('&', '&amp;', $pagelinkedto);
3187
3188 // Check if the page linked to is in our site
3189 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
3190 if( !$pos1 )
3191 return new IXR_Error(0, __('Is there no link to us?'));
3192
3193 // let's find which post is linked to
3194 // FIXME: does url_to_postid() cover all these cases already?
3195 // if so, then let's use it and drop the old code.
3196 $urltest = parse_url($pagelinkedto);
3197 if ($post_ID = url_to_postid($pagelinkedto)) {
3198 $way = 'url_to_postid()';
3199 } elseif (preg_match('#p/[0-9]{1,}#', $urltest['path'], $match)) {
3200 // the path defines the post_ID (archives/p/XXXX)
3201 $blah = explode('/', $match[0]);
3202 $post_ID = (int) $blah[1];
3203 $way = 'from the path';
3204 } elseif (preg_match('#p=[0-9]{1,}#', $urltest['query'], $match)) {
3205 // the querystring defines the post_ID (?p=XXXX)
3206 $blah = explode('=', $match[0]);
3207 $post_ID = (int) $blah[1];
3208 $way = 'from the querystring';
3209 } elseif (isset($urltest['fragment'])) {
3210 // an #anchor is there, it's either...
3211 if (intval($urltest['fragment'])) {
3212 // ...an integer #XXXX (simpliest case)
3213 $post_ID = (int) $urltest['fragment'];
3214 $way = 'from the fragment (numeric)';
3215 } elseif (preg_match('/post-[0-9]+/',$urltest['fragment'])) {
3216 // ...a post id in the form 'post-###'
3217 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
3218 $way = 'from the fragment (post-###)';
3219 } elseif (is_string($urltest['fragment'])) {
3220 // ...or a string #title, a little more complicated
3221 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
3222 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title);
3223 if (! ($post_ID = $wpdb->get_var($sql)) ) {
3224 // returning unknown error '0' is better than die()ing
3225 return new IXR_Error(0, '');
3226 }
3227 $way = 'from the fragment (title)';
3228 }
3229 } else {
3230 // TODO: Attempt to extract a post ID from the given URL
3231 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.'));
3232 }
3233 $post_ID = (int) $post_ID;
3234
3235
3236 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'");
3237
3238 $post = get_post($post_ID);
3239
3240 if ( !$post ) // Post_ID not found
3241 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.'));
3242
3243 if ( $post_ID == url_to_postid($pagelinkedfrom) )
3244 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.'));
3245
3246 // Check if pings are on
3247 if ( !pings_open($post) )
3248 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.'));
3249
3250 // Let's check that the remote site didn't already pingback this entry
3251 $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) );
3252
3253 if ( $wpdb->num_rows ) // We already have a Pingback from this URL
3254 return new IXR_Error(48, __('The pingback has already been registered.'));
3255
3256 // very stupid, but gives time to the 'from' server to publish !
3257 sleep(1);
3258
3259 // Let's check the remote site
3260
3261
3262// First, make sure we're not being used for DDoS!
3263
3264if (gethostbyname(parse_url($pagelinkedfrom, PHP_URL_HOST)) <> $_SERVER['REMOTE_ADDR']) {
3265 die ("Sorry, you will have to send this from your actual IP.");
3266 mail("office@polimedia.us","trackback",$pagelinkedfrom);
3267}
3268
3269 $linea = wp_remote_fopen( $pagelinkedfrom );
3270 if ( !$linea )
3271 return new IXR_Error(16, __('The source URL does not exist.'));
3272
3273 $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);
3274
3275 // Work around bug in strip_tags():
3276 $linea = str_replace('<!DOC', '<DOC', $linea);
3277 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
3278 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
3279
3280 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
3281 $title = $matchtitle[1];
3282 if ( empty( $title ) )
3283 return new IXR_Error(32, __('We cannot find a title on that page.'));
3284
3285 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need
3286
3287 $p = explode( "\n\n", $linea );
3288
3289 $preg_target = preg_quote($pagelinkedto);
3290
3291 foreach ( $p as $para ) {
3292 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
3293 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
3294
3295 // If the URL isn't in a link context, keep looking
3296 if ( empty($context) )
3297 continue;
3298
3299 // We're going to use this fake tag to mark the context in a bit
3300 // the marker is needed in case the link text appears more than once in the paragraph
3301 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
3302
3303 // prevent really long link text
3304 if ( strlen($context[1]) > 100 )
3305 $context[1] = substr($context[1], 0, 100) . '...';
3306
3307 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker
3308 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
3309 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker
3310 $excerpt = trim($excerpt);
3311 $preg_marker = preg_quote($marker);
3312 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
3313 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
3314 break;
3315 }
3316 }
3317
3318 if ( empty($context) ) // Link to target not found
3319 return new IXR_Error(17, __('The source URL does not contain a link to the target URL, and so cannot be used as a source.'));
3320
3321 $pagelinkedfrom = str_replace('&', '&amp;', $pagelinkedfrom);
3322
3323 $context = '[...] ' . wp_specialchars( $excerpt ) . ' [...]';
3324 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );
3325
3326 $comment_post_ID = (int) $post_ID;
3327 $comment_author = $title;
3328 $this->escape($comment_author);
3329 $comment_author_url = $pagelinkedfrom;
3330 $comment_content = $context;
3331 $this->escape($comment_content);
3332 $comment_type = 'pingback';
3333
3334 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type');
3335
3336 $comment_ID = wp_new_comment($commentdata);
3337 do_action('pingback_post', $comment_ID);
3338
3339 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);
3340 }
3341
3342 /**
3343 * Retrieve array of URLs that pingbacked the given URL.
3344 *
3345 * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
3346 *
3347 * @since 1.5.0
3348 *
3349 * @param array $args Method parameters.
3350 * @return array
3351 */
3352 function pingback_extensions_getPingbacks($args) {
3353
3354 global $wpdb;
3355
3356 do_action('xmlrpc_call', 'pingback.extensions.getPingsbacks');
3357
3358 $this->escape($args);
3359
3360 $url = $args;
3361
3362 $post_ID = url_to_postid($url);
3363 if (!$post_ID) {
3364 // We aren't sure that the resource is available and/or pingback enabled
3365 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.'));
3366 }
3367
3368 $actual_post = wp_get_single_post($post_ID, ARRAY_A);
3369
3370 if (!$actual_post) {
3371 // No such post = resource not found
3372 return new IXR_Error(32, __('The specified target URL does not exist.'));
3373 }
3374
3375 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
3376
3377 if (!$comments) {
3378 return array();
3379 }
3380
3381 $pingbacks = array();
3382 foreach($comments as $comment) {
3383 if ( 'pingback' == $comment->comment_type )
3384 $pingbacks[] = $comment->comment_author_url;
3385 }
3386
3387 return $pingbacks;
3388 }
3389}
3390
3391$wp_xmlrpc_server = new wp_xmlrpc_server();
3392
3393?>