PageRenderTime 31ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-includes/class-wp-xmlrpc-server.php

https://bitbucket.org/masidev/eapinfo
PHP | 5108 lines | 3127 code | 862 blank | 1119 comment | 568 complexity | 6419200edbad5ce39aac4f575f035bad MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * XML-RPC protocol support for WordPress
  4. *
  5. * @package WordPress
  6. */
  7. /**
  8. * WordPress XMLRPC server implementation.
  9. *
  10. * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and
  11. * pingback. Additional WordPress API for managing comments, pages, posts,
  12. * options, etc.
  13. *
  14. * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the
  15. * administration panels.
  16. *
  17. * @package WordPress
  18. * @subpackage Publishing
  19. * @since 1.5.0
  20. */
  21. class wp_xmlrpc_server extends IXR_Server {
  22. /**
  23. * Register all of the XMLRPC methods that XMLRPC server understands.
  24. *
  25. * Sets up server and method property. Passes XMLRPC
  26. * methods through the 'xmlrpc_methods' filter to allow plugins to extend
  27. * or replace XMLRPC methods.
  28. *
  29. * @since 1.5.0
  30. *
  31. * @return wp_xmlrpc_server
  32. */
  33. function __construct() {
  34. $this->methods = array(
  35. // WordPress API
  36. 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
  37. 'wp.newPost' => 'this:wp_newPost',
  38. 'wp.editPost' => 'this:wp_editPost',
  39. 'wp.deletePost' => 'this:wp_deletePost',
  40. 'wp.getPost' => 'this:wp_getPost',
  41. 'wp.getPosts' => 'this:wp_getPosts',
  42. 'wp.newTerm' => 'this:wp_newTerm',
  43. 'wp.editTerm' => 'this:wp_editTerm',
  44. 'wp.deleteTerm' => 'this:wp_deleteTerm',
  45. 'wp.getTerm' => 'this:wp_getTerm',
  46. 'wp.getTerms' => 'this:wp_getTerms',
  47. 'wp.getTaxonomy' => 'this:wp_getTaxonomy',
  48. 'wp.getTaxonomies' => 'this:wp_getTaxonomies',
  49. 'wp.getPage' => 'this:wp_getPage',
  50. 'wp.getPages' => 'this:wp_getPages',
  51. 'wp.newPage' => 'this:wp_newPage',
  52. 'wp.deletePage' => 'this:wp_deletePage',
  53. 'wp.editPage' => 'this:wp_editPage',
  54. 'wp.getPageList' => 'this:wp_getPageList',
  55. 'wp.getAuthors' => 'this:wp_getAuthors',
  56. 'wp.getCategories' => 'this:mw_getCategories', // Alias
  57. 'wp.getTags' => 'this:wp_getTags',
  58. 'wp.newCategory' => 'this:wp_newCategory',
  59. 'wp.deleteCategory' => 'this:wp_deleteCategory',
  60. 'wp.suggestCategories' => 'this:wp_suggestCategories',
  61. 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias
  62. 'wp.getCommentCount' => 'this:wp_getCommentCount',
  63. 'wp.getPostStatusList' => 'this:wp_getPostStatusList',
  64. 'wp.getPageStatusList' => 'this:wp_getPageStatusList',
  65. 'wp.getPageTemplates' => 'this:wp_getPageTemplates',
  66. 'wp.getOptions' => 'this:wp_getOptions',
  67. 'wp.setOptions' => 'this:wp_setOptions',
  68. 'wp.getComment' => 'this:wp_getComment',
  69. 'wp.getComments' => 'this:wp_getComments',
  70. 'wp.deleteComment' => 'this:wp_deleteComment',
  71. 'wp.editComment' => 'this:wp_editComment',
  72. 'wp.newComment' => 'this:wp_newComment',
  73. 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
  74. 'wp.getMediaItem' => 'this:wp_getMediaItem',
  75. 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary',
  76. 'wp.getPostFormats' => 'this:wp_getPostFormats',
  77. 'wp.getPostType' => 'this:wp_getPostType',
  78. 'wp.getPostTypes' => 'this:wp_getPostTypes',
  79. // Blogger API
  80. 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
  81. 'blogger.getUserInfo' => 'this:blogger_getUserInfo',
  82. 'blogger.getPost' => 'this:blogger_getPost',
  83. 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
  84. 'blogger.getTemplate' => 'this:blogger_getTemplate',
  85. 'blogger.setTemplate' => 'this:blogger_setTemplate',
  86. 'blogger.newPost' => 'this:blogger_newPost',
  87. 'blogger.editPost' => 'this:blogger_editPost',
  88. 'blogger.deletePost' => 'this:blogger_deletePost',
  89. // MetaWeblog API (with MT extensions to structs)
  90. 'metaWeblog.newPost' => 'this:mw_newPost',
  91. 'metaWeblog.editPost' => 'this:mw_editPost',
  92. 'metaWeblog.getPost' => 'this:mw_getPost',
  93. 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
  94. 'metaWeblog.getCategories' => 'this:mw_getCategories',
  95. 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
  96. // MetaWeblog API aliases for Blogger API
  97. // see http://www.xmlrpc.com/stories/storyReader$2460
  98. 'metaWeblog.deletePost' => 'this:blogger_deletePost',
  99. 'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
  100. 'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
  101. 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
  102. // MovableType API
  103. 'mt.getCategoryList' => 'this:mt_getCategoryList',
  104. 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
  105. 'mt.getPostCategories' => 'this:mt_getPostCategories',
  106. 'mt.setPostCategories' => 'this:mt_setPostCategories',
  107. 'mt.supportedMethods' => 'this:mt_supportedMethods',
  108. 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
  109. 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
  110. 'mt.publishPost' => 'this:mt_publishPost',
  111. // PingBack
  112. 'pingback.ping' => 'this:pingback_ping',
  113. 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
  114. 'demo.sayHello' => 'this:sayHello',
  115. 'demo.addTwoNumbers' => 'this:addTwoNumbers'
  116. );
  117. $this->initialise_blog_option_info();
  118. $this->methods = apply_filters('xmlrpc_methods', $this->methods);
  119. }
  120. function serve_request() {
  121. $this->IXR_Server($this->methods);
  122. }
  123. /**
  124. * Test XMLRPC API by saying, "Hello!" to client.
  125. *
  126. * @since 1.5.0
  127. *
  128. * @param array $args Method Parameters.
  129. * @return string
  130. */
  131. function sayHello($args) {
  132. return 'Hello!';
  133. }
  134. /**
  135. * Test XMLRPC API by adding two numbers for client.
  136. *
  137. * @since 1.5.0
  138. *
  139. * @param array $args Method Parameters.
  140. * @return int
  141. */
  142. function addTwoNumbers($args) {
  143. $number1 = $args[0];
  144. $number2 = $args[1];
  145. return $number1 + $number2;
  146. }
  147. /**
  148. * Check user's credentials.
  149. *
  150. * @since 1.5.0
  151. *
  152. * @param string $user_login User's username.
  153. * @param string $user_pass User's password.
  154. * @return bool Whether authentication passed.
  155. * @deprecated use wp_xmlrpc_server::login
  156. * @see wp_xmlrpc_server::login
  157. */
  158. function login_pass_ok($user_login, $user_pass) {
  159. if ( !get_option( 'enable_xmlrpc' ) ) {
  160. $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'), admin_url('options-writing.php') ) );
  161. return false;
  162. }
  163. if (!user_pass_ok($user_login, $user_pass)) {
  164. $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
  165. return false;
  166. }
  167. return true;
  168. }
  169. /**
  170. * Log user in.
  171. *
  172. * @since 2.8
  173. *
  174. * @param string $username User's username.
  175. * @param string $password User's password.
  176. * @return mixed WP_User object if authentication passed, false otherwise
  177. */
  178. function login($username, $password) {
  179. if ( !get_option( 'enable_xmlrpc' ) ) {
  180. $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'), admin_url('options-writing.php') ) );
  181. return false;
  182. }
  183. $user = wp_authenticate($username, $password);
  184. if (is_wp_error($user)) {
  185. $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
  186. return false;
  187. }
  188. wp_set_current_user( $user->ID );
  189. return $user;
  190. }
  191. /**
  192. * Sanitize string or array of strings for database.
  193. *
  194. * @since 1.5.2
  195. *
  196. * @param string|array $array Sanitize single string or array of strings.
  197. * @return string|array Type matches $array and sanitized for the database.
  198. */
  199. function escape(&$array) {
  200. global $wpdb;
  201. if (!is_array($array)) {
  202. return($wpdb->escape($array));
  203. } else {
  204. foreach ( (array) $array as $k => $v ) {
  205. if ( is_array($v) ) {
  206. $this->escape($array[$k]);
  207. } else if ( is_object($v) ) {
  208. //skip
  209. } else {
  210. $array[$k] = $wpdb->escape($v);
  211. }
  212. }
  213. }
  214. }
  215. /**
  216. * Retrieve custom fields for post.
  217. *
  218. * @since 2.5.0
  219. *
  220. * @param int $post_id Post ID.
  221. * @return array Custom fields, if exist.
  222. */
  223. function get_custom_fields($post_id) {
  224. $post_id = (int) $post_id;
  225. $custom_fields = array();
  226. foreach ( (array) has_meta($post_id) as $meta ) {
  227. // Don't expose protected fields.
  228. if ( ! current_user_can( 'edit_post_meta', $post_id , $meta['meta_key'] ) )
  229. continue;
  230. $custom_fields[] = array(
  231. "id" => $meta['meta_id'],
  232. "key" => $meta['meta_key'],
  233. "value" => $meta['meta_value']
  234. );
  235. }
  236. return $custom_fields;
  237. }
  238. /**
  239. * Set custom fields for post.
  240. *
  241. * @since 2.5.0
  242. *
  243. * @param int $post_id Post ID.
  244. * @param array $fields Custom fields.
  245. */
  246. function set_custom_fields($post_id, $fields) {
  247. $post_id = (int) $post_id;
  248. foreach ( (array) $fields as $meta ) {
  249. if ( isset($meta['id']) ) {
  250. $meta['id'] = (int) $meta['id'];
  251. $pmeta = get_metadata_by_mid( 'post', $meta['id'] );
  252. if ( isset($meta['key']) ) {
  253. $meta['key'] = stripslashes( $meta['key'] );
  254. if ( $meta['key'] != $pmeta->meta_key )
  255. continue;
  256. $meta['value'] = stripslashes_deep( $meta['value'] );
  257. if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) )
  258. update_metadata_by_mid( 'post', $meta['id'], $meta['value'] );
  259. } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) {
  260. delete_metadata_by_mid( 'post', $meta['id'] );
  261. }
  262. } elseif ( current_user_can( 'add_post_meta', $post_id, stripslashes( $meta['key'] ) ) ) {
  263. add_post_meta( $post_id, $meta['key'], $meta['value'] );
  264. }
  265. }
  266. }
  267. /**
  268. * Set up blog options property.
  269. *
  270. * Passes property through 'xmlrpc_blog_options' filter.
  271. *
  272. * @since 2.6.0
  273. */
  274. function initialise_blog_option_info() {
  275. global $wp_version;
  276. $this->blog_options = array(
  277. // Read only options
  278. 'software_name' => array(
  279. 'desc' => __( 'Software Name' ),
  280. 'readonly' => true,
  281. 'value' => 'WordPress'
  282. ),
  283. 'software_version' => array(
  284. 'desc' => __( 'Software Version' ),
  285. 'readonly' => true,
  286. 'value' => $wp_version
  287. ),
  288. 'blog_url' => array(
  289. 'desc' => __( 'Site URL' ),
  290. 'readonly' => true,
  291. 'option' => 'siteurl'
  292. ),
  293. 'image_default_link_type' => array(
  294. 'desc' => __( 'Image default link type' ),
  295. 'readonly' => true,
  296. 'option' => 'image_default_link_type'
  297. ),
  298. 'image_default_size' => array(
  299. 'desc' => __( 'Image default size' ),
  300. 'readonly' => true,
  301. 'option' => 'image_default_size'
  302. ),
  303. 'image_default_align' => array(
  304. 'desc' => __( 'Image default align' ),
  305. 'readonly' => true,
  306. 'option' => 'image_default_align'
  307. ),
  308. 'template' => array(
  309. 'desc' => __( 'Template' ),
  310. 'readonly' => true,
  311. 'option' => 'template'
  312. ),
  313. 'stylesheet' => array(
  314. 'desc' => __( 'Stylesheet' ),
  315. 'readonly' => true,
  316. 'option' => 'stylesheet'
  317. ),
  318. 'post_thumbnail' => array(
  319. 'desc' => __('Post Thumbnail'),
  320. 'readonly' => true,
  321. 'value' => current_theme_supports( 'post-thumbnails' )
  322. ),
  323. // Updatable options
  324. 'time_zone' => array(
  325. 'desc' => __( 'Time Zone' ),
  326. 'readonly' => false,
  327. 'option' => 'gmt_offset'
  328. ),
  329. 'blog_title' => array(
  330. 'desc' => __( 'Site Title' ),
  331. 'readonly' => false,
  332. 'option' => 'blogname'
  333. ),
  334. 'blog_tagline' => array(
  335. 'desc' => __( 'Site Tagline' ),
  336. 'readonly' => false,
  337. 'option' => 'blogdescription'
  338. ),
  339. 'date_format' => array(
  340. 'desc' => __( 'Date Format' ),
  341. 'readonly' => false,
  342. 'option' => 'date_format'
  343. ),
  344. 'time_format' => array(
  345. 'desc' => __( 'Time Format' ),
  346. 'readonly' => false,
  347. 'option' => 'time_format'
  348. ),
  349. 'users_can_register' => array(
  350. 'desc' => __( 'Allow new users to sign up' ),
  351. 'readonly' => false,
  352. 'option' => 'users_can_register'
  353. ),
  354. 'thumbnail_size_w' => array(
  355. 'desc' => __( 'Thumbnail Width' ),
  356. 'readonly' => false,
  357. 'option' => 'thumbnail_size_w'
  358. ),
  359. 'thumbnail_size_h' => array(
  360. 'desc' => __( 'Thumbnail Height' ),
  361. 'readonly' => false,
  362. 'option' => 'thumbnail_size_h'
  363. ),
  364. 'thumbnail_crop' => array(
  365. 'desc' => __( 'Crop thumbnail to exact dimensions' ),
  366. 'readonly' => false,
  367. 'option' => 'thumbnail_crop'
  368. ),
  369. 'medium_size_w' => array(
  370. 'desc' => __( 'Medium size image width' ),
  371. 'readonly' => false,
  372. 'option' => 'medium_size_w'
  373. ),
  374. 'medium_size_h' => array(
  375. 'desc' => __( 'Medium size image height' ),
  376. 'readonly' => false,
  377. 'option' => 'medium_size_h'
  378. ),
  379. 'large_size_w' => array(
  380. 'desc' => __( 'Large size image width' ),
  381. 'readonly' => false,
  382. 'option' => 'large_size_w'
  383. ),
  384. 'large_size_h' => array(
  385. 'desc' => __( 'Large size image height' ),
  386. 'readonly' => false,
  387. 'option' => 'large_size_h'
  388. ),
  389. 'default_comment_status' => array(
  390. 'desc' => __( 'Allow people to post comments on new articles' ),
  391. 'readonly' => false,
  392. 'option' => 'default_comment_status'
  393. ),
  394. 'default_ping_status' => array(
  395. 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks)' ),
  396. 'readonly' => false,
  397. 'option' => 'default_ping_status'
  398. )
  399. );
  400. $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
  401. }
  402. /**
  403. * Retrieve the blogs of the user.
  404. *
  405. * @since 2.6.0
  406. *
  407. * @param array $args Method parameters. Contains:
  408. * - username
  409. * - password
  410. * @return array. Contains:
  411. * - 'isAdmin'
  412. * - 'url'
  413. * - 'blogid'
  414. * - 'blogName'
  415. * - 'xmlrpc' - url of xmlrpc endpoint
  416. */
  417. function wp_getUsersBlogs( $args ) {
  418. global $current_site;
  419. // If this isn't on WPMU then just use blogger_getUsersBlogs
  420. if ( !is_multisite() ) {
  421. array_unshift( $args, 1 );
  422. return $this->blogger_getUsersBlogs( $args );
  423. }
  424. $this->escape( $args );
  425. $username = $args[0];
  426. $password = $args[1];
  427. if ( !$user = $this->login($username, $password) )
  428. return $this->error;
  429. do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
  430. $blogs = (array) get_blogs_of_user( $user->ID );
  431. $struct = array();
  432. foreach ( $blogs as $blog ) {
  433. // Don't include blogs that aren't hosted at this site
  434. if ( $blog->site_id != $current_site->id )
  435. continue;
  436. $blog_id = $blog->userblog_id;
  437. switch_to_blog($blog_id);
  438. $is_admin = current_user_can('manage_options');
  439. $struct[] = array(
  440. 'isAdmin' => $is_admin,
  441. 'url' => get_option( 'home' ) . '/',
  442. 'blogid' => (string) $blog_id,
  443. 'blogName' => get_option( 'blogname' ),
  444. 'xmlrpc' => site_url( 'xmlrpc.php' )
  445. );
  446. restore_current_blog();
  447. }
  448. return $struct;
  449. }
  450. /**
  451. * Checks if the method received at least the minimum number of arguments.
  452. *
  453. * @since 3.4.0
  454. *
  455. * @param string|array $args Sanitize single string or array of strings.
  456. * @param int $count Minimum number of arguments.
  457. * @return boolean if $args contains at least $count arguments.
  458. */
  459. protected function minimum_args( $args, $count ) {
  460. if ( count( $args ) < $count ) {
  461. $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) );
  462. return false;
  463. }
  464. return true;
  465. }
  466. /**
  467. * Prepares taxonomy data for return in an XML-RPC object.
  468. *
  469. * @access protected
  470. *
  471. * @param object $taxonomy The unprepared taxonomy data
  472. * @param array $fields The subset of taxonomy fields to return
  473. * @return array The prepared taxonomy data
  474. */
  475. protected function _prepare_taxonomy( $taxonomy, $fields ) {
  476. $_taxonomy = array(
  477. 'name' => $taxonomy->name,
  478. 'label' => $taxonomy->label,
  479. 'hierarchical' => (bool) $taxonomy->hierarchical,
  480. 'public' => (bool) $taxonomy->public,
  481. 'show_ui' => (bool) $taxonomy->show_ui,
  482. '_builtin' => (bool) $taxonomy->_builtin,
  483. );
  484. if ( in_array( 'labels', $fields ) )
  485. $_taxonomy['labels'] = (array) $taxonomy->labels;
  486. if ( in_array( 'cap', $fields ) )
  487. $_taxonomy['cap'] = (array) $taxonomy->cap;
  488. if ( in_array( 'object_type', $fields ) )
  489. $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type );
  490. return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields );
  491. }
  492. /**
  493. * Prepares term data for return in an XML-RPC object.
  494. *
  495. * @access protected
  496. *
  497. * @param array|object $term The unprepared term data
  498. * @return array The prepared term data
  499. */
  500. protected function _prepare_term( $term ) {
  501. $_term = $term;
  502. if ( ! is_array( $_term) )
  503. $_term = get_object_vars( $_term );
  504. // For Intergers which may be largeer than XMLRPC supports ensure we return strings.
  505. $_term['term_id'] = strval( $_term['term_id'] );
  506. $_term['term_group'] = strval( $_term['term_group'] );
  507. $_term['term_taxonomy_id'] = strval( $_term['term_taxonomy_id'] );
  508. $_term['parent'] = strval( $_term['parent'] );
  509. // Count we are happy to return as an Integer because people really shouldn't use Terms that much.
  510. $_term['count'] = intval( $_term['count'] );
  511. return apply_filters( 'xmlrpc_prepare_term', $_term, $term );
  512. }
  513. /**
  514. * Convert a WordPress date string to an IXR_Date object.
  515. *
  516. * @access protected
  517. *
  518. * @param string $date
  519. * @return IXR_Date
  520. */
  521. protected function _convert_date( $date ) {
  522. if ( $date === '0000-00-00 00:00:00' ) {
  523. return new IXR_Date( '00000000T00:00:00Z' );
  524. }
  525. return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) );
  526. }
  527. /**
  528. * Convert a WordPress GMT date string to an IXR_Date object.
  529. *
  530. * @access protected
  531. *
  532. * @param string $date_gmt
  533. * @param string $date
  534. * @return IXR_Date
  535. */
  536. protected function _convert_date_gmt( $date_gmt, $date ) {
  537. if ( $date !== '0000-00-00 00:00:00' && $date_gmt === '0000-00-00 00:00:00' ) {
  538. return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) );
  539. }
  540. return $this->_convert_date( $date_gmt );
  541. }
  542. /**
  543. * Prepares post data for return in an XML-RPC object.
  544. *
  545. * @access protected
  546. *
  547. * @param array $post The unprepared post data
  548. * @param array $fields The subset of post type fields to return
  549. * @return array The prepared post data
  550. */
  551. protected function _prepare_post( $post, $fields ) {
  552. // holds the data for this post. built up based on $fields
  553. $_post = array( 'post_id' => strval( $post['ID'] ) );
  554. // prepare common post fields
  555. $post_fields = array(
  556. 'post_title' => $post['post_title'],
  557. 'post_date' => $this->_convert_date( $post['post_date'] ),
  558. 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ),
  559. 'post_modified' => $this->_convert_date( $post['post_modified'] ),
  560. 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ),
  561. 'post_status' => $post['post_status'],
  562. 'post_type' => $post['post_type'],
  563. 'post_name' => $post['post_name'],
  564. 'post_author' => $post['post_author'],
  565. 'post_password' => $post['post_password'],
  566. 'post_excerpt' => $post['post_excerpt'],
  567. 'post_content' => $post['post_content'],
  568. 'post_parent' => strval( $post['post_parent'] ),
  569. 'post_mime_type' => $post['post_mime_type'],
  570. 'link' => post_permalink( $post['ID'] ),
  571. 'guid' => $post['guid'],
  572. 'menu_order' => intval( $post['menu_order'] ),
  573. 'comment_status' => $post['comment_status'],
  574. 'ping_status' => $post['ping_status'],
  575. 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
  576. );
  577. // Thumbnail
  578. $post_fields['post_thumbnail'] = array();
  579. $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
  580. if ( $thumbnail_id ) {
  581. $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail';
  582. $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
  583. }
  584. // Consider future posts as published
  585. if ( $post_fields['post_status'] === 'future' )
  586. $post_fields['post_status'] = 'publish';
  587. // Fill in blank post format
  588. $post_fields['post_format'] = get_post_format( $post['ID'] );
  589. if ( empty( $post_fields['post_format'] ) )
  590. $post_fields['post_format'] = 'standard';
  591. // Merge requested $post_fields fields into $_post
  592. if ( in_array( 'post', $fields ) ) {
  593. $_post = array_merge( $_post, $post_fields );
  594. } else {
  595. $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
  596. $_post = array_merge( $_post, $requested_fields );
  597. }
  598. $all_taxonomy_fields = in_array( 'taxonomies', $fields );
  599. if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
  600. $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' );
  601. $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
  602. $_post['terms'] = array();
  603. foreach ( $terms as $term ) {
  604. $_post['terms'][] = $this->_prepare_term( $term );
  605. }
  606. }
  607. if ( in_array( 'custom_fields', $fields ) )
  608. $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] );
  609. if ( in_array( 'enclosure', $fields ) ) {
  610. $_post['enclosure'] = array();
  611. $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' );
  612. if ( ! empty( $enclosures ) ) {
  613. $encdata = explode( "\n", $enclosures[0] );
  614. $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) );
  615. $_post['enclosure']['length'] = (int) trim( $encdata[1] );
  616. $_post['enclosure']['type'] = trim( $encdata[2] );
  617. }
  618. }
  619. return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields );
  620. }
  621. /**
  622. * Prepares post data for return in an XML-RPC object.
  623. *
  624. * @access protected
  625. *
  626. * @param object $post_type Post type object
  627. * @param array $fields The subset of post fields to return
  628. * @return array The prepared post type data
  629. */
  630. protected function _prepare_post_type( $post_type, $fields ) {
  631. $_post_type = array(
  632. 'name' => $post_type->name,
  633. 'label' => $post_type->label,
  634. 'hierarchical' => (bool) $post_type->hierarchical,
  635. 'public' => (bool) $post_type->public,
  636. 'show_ui' => (bool) $post_type->show_ui,
  637. '_builtin' => (bool) $post_type->_builtin,
  638. 'has_archive' => (bool) $post_type->has_archive,
  639. 'supports' => get_all_post_type_supports( $post_type->name ),
  640. );
  641. if ( in_array( 'labels', $fields ) ) {
  642. $_post_type['labels'] = (array) $post_type->labels;
  643. }
  644. if ( in_array( 'cap', $fields ) ) {
  645. $_post_type['cap'] = (array) $post_type->cap;
  646. $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap;
  647. }
  648. if ( in_array( 'menu', $fields ) ) {
  649. $_post_type['menu_position'] = (int) $post_type->menu_position;
  650. $_post_type['menu_icon'] = $post_type->menu_icon;
  651. $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu;
  652. }
  653. if ( in_array( 'taxonomies', $fields ) )
  654. $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' );
  655. return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type );
  656. }
  657. /**
  658. * Prepares media item data for return in an XML-RPC object.
  659. *
  660. * @access protected
  661. *
  662. * @param object $media_item The unprepared media item data
  663. * @param string $thumbnail_size The image size to use for the thumbnail URL
  664. * @return array The prepared media item data
  665. */
  666. protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) {
  667. $_media_item = array(
  668. 'attachment_id' => strval( $media_item->ID ),
  669. 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
  670. 'parent' => $media_item->post_parent,
  671. 'link' => wp_get_attachment_url( $media_item->ID ),
  672. 'title' => $media_item->post_title,
  673. 'caption' => $media_item->post_excerpt,
  674. 'description' => $media_item->post_content,
  675. 'metadata' => wp_get_attachment_metadata( $media_item->ID ),
  676. );
  677. $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
  678. if ( $thumbnail_src )
  679. $_media_item['thumbnail'] = $thumbnail_src[0];
  680. else
  681. $_media_item['thumbnail'] = $_media_item['link'];
  682. return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
  683. }
  684. /**
  685. * Prepares page data for return in an XML-RPC object.
  686. *
  687. * @access protected
  688. *
  689. * @param object $page The unprepared page data
  690. * @return array The prepared page data
  691. */
  692. protected function _prepare_page( $page ) {
  693. // Get all of the page content and link.
  694. $full_page = get_extended( $page->post_content );
  695. $link = post_permalink( $page->ID );
  696. // Get info the page parent if there is one.
  697. $parent_title = "";
  698. if ( ! empty( $page->post_parent ) ) {
  699. $parent = get_page( $page->post_parent );
  700. $parent_title = $parent->post_title;
  701. }
  702. // Determine comment and ping settings.
  703. $allow_comments = comments_open( $page->ID ) ? 1 : 0;
  704. $allow_pings = pings_open( $page->ID ) ? 1 : 0;
  705. // Format page date.
  706. $page_date = $this->_convert_date( $page->post_date );
  707. $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date );
  708. // Pull the categories info together.
  709. $categories = array();
  710. foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) {
  711. $categories[] = get_cat_name( $cat_id );
  712. }
  713. // Get the author info.
  714. $author = get_userdata( $page->post_author );
  715. $page_template = get_page_template_slug( $page->ID );
  716. if ( empty( $page_template ) )
  717. $page_template = 'default';
  718. $_page = array(
  719. 'dateCreated' => $page_date,
  720. 'userid' => $page->post_author,
  721. 'page_id' => $page->ID,
  722. 'page_status' => $page->post_status,
  723. 'description' => $full_page['main'],
  724. 'title' => $page->post_title,
  725. 'link' => $link,
  726. 'permaLink' => $link,
  727. 'categories' => $categories,
  728. 'excerpt' => $page->post_excerpt,
  729. 'text_more' => $full_page['extended'],
  730. 'mt_allow_comments' => $allow_comments,
  731. 'mt_allow_pings' => $allow_pings,
  732. 'wp_slug' => $page->post_name,
  733. 'wp_password' => $page->post_password,
  734. 'wp_author' => $author->display_name,
  735. 'wp_page_parent_id' => $page->post_parent,
  736. 'wp_page_parent_title' => $parent_title,
  737. 'wp_page_order' => $page->menu_order,
  738. 'wp_author_id' => (string) $author->ID,
  739. 'wp_author_display_name' => $author->display_name,
  740. 'date_created_gmt' => $page_date_gmt,
  741. 'custom_fields' => $this->get_custom_fields( $page->ID ),
  742. 'wp_page_template' => $page_template
  743. );
  744. return apply_filters( 'xmlrpc_prepare_page', $_page, $page );
  745. }
  746. /**
  747. * Prepares comment data for return in an XML-RPC object.
  748. *
  749. * @access protected
  750. *
  751. * @param object $comment The unprepared comment data
  752. * @return array The prepared comment data
  753. */
  754. protected function _prepare_comment( $comment ) {
  755. // Format page date.
  756. $comment_date = $this->_convert_date( $comment->comment_date );
  757. $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date );
  758. if ( '0' == $comment->comment_approved )
  759. $comment_status = 'hold';
  760. else if ( 'spam' == $comment->comment_approved )
  761. $comment_status = 'spam';
  762. else if ( '1' == $comment->comment_approved )
  763. $comment_status = 'approve';
  764. else
  765. $comment_status = $comment->comment_approved;
  766. $_comment = array(
  767. 'date_created_gmt' => $comment_date_gmt,
  768. 'user_id' => $comment->user_id,
  769. 'comment_id' => $comment->comment_ID,
  770. 'parent' => $comment->comment_parent,
  771. 'status' => $comment_status,
  772. 'content' => $comment->comment_content,
  773. 'link' => get_comment_link($comment),
  774. 'post_id' => $comment->comment_post_ID,
  775. 'post_title' => get_the_title($comment->comment_post_ID),
  776. 'author' => $comment->comment_author,
  777. 'author_url' => $comment->comment_author_url,
  778. 'author_email' => $comment->comment_author_email,
  779. 'author_ip' => $comment->comment_author_IP,
  780. 'type' => $comment->comment_type,
  781. );
  782. return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment );
  783. }
  784. /**
  785. * Create a new post for any registered post type.
  786. *
  787. * @since 3.4.0
  788. *
  789. * @param array $args Method parameters. Contains:
  790. * - int $blog_id
  791. * - string $username
  792. * - string $password
  793. * - array $content_struct
  794. * $content_struct can contain:
  795. * - post_type (default: 'post')
  796. * - post_status (default: 'draft')
  797. * - post_title
  798. * - post_author
  799. * - post_exerpt
  800. * - post_content
  801. * - post_date_gmt | post_date
  802. * - post_format
  803. * - post_password
  804. * - comment_status - can be 'open' | 'closed'
  805. * - ping_status - can be 'open' | 'closed'
  806. * - sticky
  807. * - post_thumbnail - ID of a media item to use as the post thumbnail/featured image
  808. * - custom_fields - array, with each element containing 'key' and 'value'
  809. * - terms - array, with taxonomy names as keys and arrays of term IDs as values
  810. * - terms_names - array, with taxonomy names as keys and arrays of term names as values
  811. * - enclosure
  812. * - any other fields supported by wp_insert_post()
  813. * @return string post_id
  814. */
  815. function wp_newPost( $args ) {
  816. if ( ! $this->minimum_args( $args, 4 ) )
  817. return $this->error;
  818. $this->escape( $args );
  819. $blog_id = (int) $args[0];
  820. $username = $args[1];
  821. $password = $args[2];
  822. $content_struct = $args[3];
  823. if ( ! $user = $this->login( $username, $password ) )
  824. return $this->error;
  825. do_action( 'xmlrpc_call', 'wp.newPost' );
  826. unset( $content_struct['ID'] );
  827. return $this->_insert_post( $user, $content_struct );
  828. }
  829. /**
  830. * Helper method for filtering out elements from an array.
  831. *
  832. * @since 3.4.0
  833. *
  834. * @param int $count Number to compare to one.
  835. */
  836. private function _is_greater_than_one( $count ) {
  837. return $count > 1;
  838. }
  839. /**
  840. * Helper method for wp_newPost and wp_editPost, containing shared logic.
  841. *
  842. * @since 3.4.0
  843. * @uses wp_insert_post()
  844. *
  845. * @param WP_User $user The post author if post_author isn't set in $content_struct.
  846. * @param array $content_struct Post data to insert.
  847. */
  848. protected function _insert_post( $user, $content_struct ) {
  849. $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0,
  850. 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' );
  851. $post_data = wp_parse_args( $content_struct, $defaults );
  852. $post_type = get_post_type_object( $post_data['post_type'] );
  853. if ( ! $post_type )
  854. return new IXR_Error( 403, __( 'Invalid post type' ) );
  855. $update = ! empty( $post_data['ID'] );
  856. if ( $update ) {
  857. if ( ! get_post( $post_data['ID'] ) )
  858. return new IXR_Error( 401, __( 'Invalid post ID.' ) );
  859. if ( ! current_user_can( $post_type->cap->edit_post, $post_data['ID'] ) )
  860. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
  861. if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) )
  862. return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
  863. } else {
  864. if ( ! current_user_can( $post_type->cap->edit_posts ) )
  865. return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
  866. }
  867. switch ( $post_data['post_status'] ) {
  868. case 'draft':
  869. case 'pending':
  870. break;
  871. case 'private':
  872. if ( ! current_user_can( $post_type->cap->publish_posts ) )
  873. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type' ) );
  874. break;
  875. case 'publish':
  876. case 'future':
  877. if ( ! current_user_can( $post_type->cap->publish_posts ) )
  878. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) );
  879. break;
  880. default:
  881. $post_data['post_status'] = 'draft';
  882. break;
  883. }
  884. if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) )
  885. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type' ) );
  886. $post_data['post_author'] = absint( $post_data['post_author'] );
  887. if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) {
  888. if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
  889. return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) );
  890. $author = get_userdata( $post_data['post_author'] );
  891. if ( ! $author )
  892. return new IXR_Error( 404, __( 'Invalid author ID.' ) );
  893. } else {
  894. $post_data['post_author'] = $user->ID;
  895. }
  896. if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' )
  897. unset( $post_data['comment_status'] );
  898. if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' )
  899. unset( $post_data['ping_status'] );
  900. // Do some timestamp voodoo
  901. if ( ! empty( $post_data['post_date_gmt'] ) ) {
  902. // We know this is supposed to be GMT, so we're going to slap that Z on there by force
  903. $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z';
  904. } elseif ( ! empty( $post_data['post_date'] ) ) {
  905. $dateCreated = $post_data['post_date']->getIso();
  906. }
  907. if ( ! empty( $dateCreated ) ) {
  908. $post_data['post_date'] = get_date_from_gmt( iso8601_to_datetime( $dateCreated ) );
  909. $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'GMT' );
  910. }
  911. if ( ! isset( $post_data['ID'] ) )
  912. $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID;
  913. $post_ID = $post_data['ID'];
  914. if ( $post_data['post_type'] == 'post' ) {
  915. // Private and password-protected posts cannot be stickied.
  916. if ( $post_data['post_status'] == 'private' || ! empty( $post_data['post_password'] ) ) {
  917. // Error if the client tried to stick the post, otherwise, silently unstick.
  918. if ( ! empty( $post_data['sticky'] ) )
  919. return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
  920. if ( $update )
  921. unstick_post( $post_ID );
  922. } elseif ( isset( $post_data['sticky'] ) ) {
  923. if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
  924. return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
  925. if ( $post_data['sticky'] )
  926. stick_post( $post_ID );
  927. else
  928. unstick_post( $post_ID );
  929. }
  930. }
  931. if ( isset( $post_data['post_thumbnail'] ) ) {
  932. // empty value deletes, non-empty value adds/updates
  933. if ( ! $post_data['post_thumbnail'] )
  934. delete_post_thumbnail( $post_ID );
  935. elseif ( ! set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ) )
  936. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
  937. unset( $content_struct['post_thumbnail'] );
  938. }
  939. if ( isset( $post_data['custom_fields'] ) )
  940. $this->set_custom_fields( $post_ID, $post_data['custom_fields'] );
  941. if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) {
  942. $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' );
  943. // accumulate term IDs from terms and terms_names
  944. $terms = array();
  945. // first validate the terms specified by ID
  946. if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) {
  947. $taxonomies = array_keys( $post_data['terms'] );
  948. // validating term ids
  949. foreach ( $taxonomies as $taxonomy ) {
  950. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
  951. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
  952. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
  953. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
  954. $term_ids = $post_data['terms'][$taxonomy];
  955. foreach ( $term_ids as $term_id ) {
  956. $term = get_term_by( 'id', $term_id, $taxonomy );
  957. if ( ! $term )
  958. return new IXR_Error( 403, __( 'Invalid term ID' ) );
  959. $terms[$taxonomy][] = (int) $term_id;
  960. }
  961. }
  962. }
  963. // now validate terms specified by name
  964. if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) {
  965. $taxonomies = array_keys( $post_data['terms_names'] );
  966. foreach ( $taxonomies as $taxonomy ) {
  967. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
  968. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
  969. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
  970. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
  971. // for hierarchical taxonomies, we can't assign a term when multiple terms in the hierarchy share the same name
  972. $ambiguous_terms = array();
  973. if ( is_taxonomy_hierarchical( $taxonomy ) ) {
  974. $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) );
  975. // count the number of terms with the same name
  976. $tax_term_names_count = array_count_values( $tax_term_names );
  977. // filter out non-ambiguous term names
  978. $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') );
  979. $ambiguous_terms = array_keys( $ambiguous_tax_term_counts );
  980. }
  981. $term_names = $post_data['terms_names'][$taxonomy];
  982. foreach ( $term_names as $term_name ) {
  983. if ( in_array( $term_name, $ambiguous_terms ) )
  984. return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) );
  985. $term = get_term_by( 'name', $term_name, $taxonomy );
  986. if ( ! $term ) {
  987. // term doesn't exist, so check that the user is allowed to create new terms
  988. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) )
  989. return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) );
  990. // create the new term
  991. $term_info = wp_insert_term( $term_name, $taxonomy );
  992. if ( is_wp_error( $term_info ) )
  993. return new IXR_Error( 500, $term_info->get_error_message() );
  994. $terms[$taxonomy][] = (int) $term_info['term_id'];
  995. } else {
  996. $terms[$taxonomy][] = (int) $term->term_id;
  997. }
  998. }
  999. }
  1000. }
  1001. $post_data['tax_input'] = $terms;
  1002. unset( $post_data['terms'], $post_data['terms_names'] );
  1003. } else {
  1004. // do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names'
  1005. unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] );
  1006. }
  1007. if ( isset( $post_data['post_format'] ) ) {
  1008. $format = set_post_format( $post_ID, $post_data['post_format'] );
  1009. if ( is_wp_error( $format ) )
  1010. return new IXR_Error( 500, $format->get_error_message() );
  1011. unset( $post_data['post_format'] );
  1012. }
  1013. // Handle enclosures
  1014. $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null;
  1015. $this->add_enclosure_if_new( $post_ID, $enclosure );
  1016. $this->attach_uploads( $post_ID, $post_data['post_content'] );
  1017. $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
  1018. $post_ID = wp_insert_post( $post_data, true );
  1019. if ( is_wp_error( $post_ID ) )
  1020. return new IXR_Error( 500, $post_ID->get_error_message() );
  1021. if ( ! $post_ID )
  1022. return new IXR_Error( 401, __( 'Sorry, your entry could not be posted. Something wrong happened.' ) );
  1023. return strval( $post_ID );
  1024. }
  1025. /**
  1026. * Edit a post for any registered post type.
  1027. *
  1028. * The $content_struct parameter only needs to contain fields that
  1029. * should be changed. All other fields will retain their existing values.
  1030. *
  1031. * @since 3.4.0
  1032. *
  1033. * @param array $args Method parameters. Contains:
  1034. * - int $blog_id
  1035. * - string $username
  1036. * - string $password
  1037. * - int $post_id
  1038. * - array $content_struct
  1039. * @return true on success
  1040. */
  1041. function wp_editPost( $args ) {
  1042. if ( ! $this->minimum_args( $args, 5 ) )
  1043. return $this->error;
  1044. $this->escape( $args );
  1045. $blog_id = (int) $args[0];
  1046. $username = $args[1];
  1047. $password = $args[2];
  1048. $post_id = (int) $args[3];
  1049. $content_struct = $args[4];
  1050. if ( ! $user = $this->login( $username, $password ) )
  1051. return $this->error;
  1052. do_action( 'xmlrpc_call', 'wp.editPost' );
  1053. $post = get_post( $post_id, ARRAY_A );
  1054. if ( empty( $post['ID'] ) )
  1055. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1056. // convert the date field back to IXR form
  1057. $post['post_date'] = $this->_convert_date( $post['post_date'] );
  1058. // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
  1059. // since _insert_post will ignore the non-GMT date if the GMT date is set
  1060. if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) )
  1061. unset( $post['post_date_gmt'] );
  1062. else
  1063. $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] );
  1064. $this->escape( $post );
  1065. $merged_content_struct = array_merge( $post, $content_struct );
  1066. $retval = $this->_insert_post( $user, $merged_content_struct );
  1067. if ( $retval instanceof IXR_Error )
  1068. return $retval;
  1069. return true;
  1070. }
  1071. /**
  1072. * Delete a post for any registered post type.
  1073. *
  1074. * @since 3.4.0
  1075. *
  1076. * @uses wp_delete_post()
  1077. * @param array $args Method parameters. Contains:
  1078. * - int $blog_id
  1079. * - string $username
  1080. * - string $password
  1081. * - int $post_id
  1082. * @return true on success
  1083. */
  1084. function wp_deletePost( $args ) {
  1085. if ( ! $this->minimum_args( $args, 4 ) )
  1086. return $this->error;
  1087. $this->escape( $args );
  1088. $blog_id = (int) $args[0];
  1089. $username = $args[1];
  1090. $password = $args[2];
  1091. $post_id = (int) $args[3];
  1092. if ( ! $user = $this->login( $username, $password ) )
  1093. return $this->error;
  1094. do_action( 'xmlrpc_call', 'wp.deletePost' );
  1095. $post = wp_get_single_post( $post_id, ARRAY_A );
  1096. if ( empty( $post['ID'] ) )
  1097. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1098. $post_type = get_post_type_object( $post['post_type'] );
  1099. if ( ! current_user_can( $post_type->cap->delete_post, $post_id ) )
  1100. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) );
  1101. $result = wp_delete_post( $post_id );
  1102. if ( ! $result )
  1103. return new IXR_Error( 500, __( 'The post cannot be deleted.' ) );
  1104. return true;
  1105. }
  1106. /**
  1107. * Retrieve a post.
  1108. *
  1109. * @since 3.4.0
  1110. *
  1111. * The optional $fields parameter specifies what fields will be included
  1112. * in the response array. This should be a list of field names. 'post_id' will
  1113. * always be included in the response regardless of the value of $fields.
  1114. *
  1115. * Instead of, or in addition to, individual field names, conceptual group
  1116. * names can be used to specify multiple fields. The available conceptual
  1117. * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
  1118. * and 'enclosure'.
  1119. *
  1120. * @uses wp_get_single_post()
  1121. * @param array $args Method parameters. Contains:
  1122. * - int $post_id
  1123. * - string $username
  1124. * - string $password
  1125. * - array $fields optional
  1126. * @return array contains (based on $fields parameter):
  1127. * - 'post_id'
  1128. * - 'post_title'
  1129. * - 'post_date'
  1130. * - 'post_date_gmt'
  1131. * - 'post_modified'
  1132. * - 'post_modified_gmt'
  1133. * - 'post_status'
  1134. * - 'post_type'
  1135. * - 'post_name'
  1136. * - 'post_author'
  1137. * - 'post_password'
  1138. * - 'post_excerpt'
  1139. * - 'post_content'
  1140. * - 'link'
  1141. * - 'comment_status'
  1142. * - 'ping_status'
  1143. * - 'sticky'
  1144. * - 'custom_fields'
  1145. * - 'terms'
  1146. * - 'categories'
  1147. * - 'tags'
  1148. * - 'enclosure'
  1149. */
  1150. function wp_getPost( $args ) {
  1151. if ( ! $this->minimum_args( $args, 4 ) )
  1152. return $this->error;
  1153. $this->escape( $args );
  1154. $blog_id = (int) $args[0];
  1155. $username = $args[1];
  1156. $password = $args[2];
  1157. $post_id = (int) $args[3];
  1158. if ( isset( $args[4] ) )
  1159. $fields = $args[4];
  1160. else
  1161. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' );
  1162. if ( ! $user = $this->login( $username, $password ) )
  1163. return $this->error;
  1164. do_action( 'xmlrpc_call', 'wp.getPost' );
  1165. $post = wp_get_single_post( $post_id, ARRAY_A );
  1166. if ( empty( $post['ID'] ) )
  1167. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1168. $post_type = get_post_type_object( $post['post_type'] );
  1169. if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) )
  1170. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  1171. return $this->_prepare_post( $post, $fields );
  1172. }
  1173. /**
  1174. * Retrieve posts.
  1175. *
  1176. * @since 3.4.0
  1177. *
  1178. * The optional $filter parameter modifies the query used to retrieve posts.
  1179. * Accepted keys are 'post_type', 'post_status', 'number', 'offset',
  1180. * 'orderby', and 'order'.
  1181. *
  1182. * The optional $fields parameter specifies what fields will be included
  1183. * in the response array.
  1184. *
  1185. * @uses wp_get_recent_posts()
  1186. * @see wp_getPost() for more on $fields
  1187. * @see get_posts() for more on $filter values
  1188. *
  1189. * @param array $args Method parameters. Contains:
  1190. * - int $blog_id
  1191. * - string $username
  1192. * - string $password
  1193. * - array $filter optional
  1194. * - array $fields optional
  1195. * @return array contains a collection of posts.
  1196. */
  1197. function wp_getPosts( $args ) {
  1198. if ( ! $this->minimum_args( $args, 3 ) )
  1199. return $this->error;
  1200. $this->escape( $args );
  1201. $blog_id = (int) $args[0];
  1202. $username = $args[1];
  1203. $password = $args[2];
  1204. $filter = isset( $args[3] ) ? $args[3] : array();
  1205. if ( isset( $args[4] ) )
  1206. $fields = $args[4];
  1207. else
  1208. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' );
  1209. if ( ! $user = $this->login( $username, $password ) )
  1210. return $this->error;
  1211. do_action( 'xmlrpc_call', 'wp.getPosts' );
  1212. $query = array();
  1213. if ( isset( $filter['post_type'] ) ) {
  1214. $post_type = get_post_type_object( $filter['post_type'] );
  1215. if ( ! ( (bool) $post_type ) )
  1216. return new IXR_Error( 403, __( 'The post type specified is not valid' ) );
  1217. } else {
  1218. $post_type = get_post_type_object( 'post' );
  1219. }
  1220. if ( ! current_user_can( $post_type->cap->edit_posts ) )
  1221. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type' ));
  1222. $query['post_type'] = $post_type->name;
  1223. if ( isset( $filter['post_status'] ) )
  1224. $query['post_status'] = $filter['post_status'];
  1225. if ( isset( $filter['number'] ) )
  1226. $query['numberposts'] = absint( $filter['number'] );
  1227. if ( isset( $filter['offset'] ) )
  1228. $query['offset'] = absint( $filter['offset'] );
  1229. if ( isset( $filter['orderby'] ) ) {
  1230. $query['orderby'] = $filter['orderby'];
  1231. if ( isset( $filter['order'] ) )
  1232. $query['order'] = $filter['order'];
  1233. }
  1234. $posts_list = wp_get_recent_posts( $query );
  1235. if ( ! $posts_list )
  1236. return array();
  1237. // holds all the posts data
  1238. $struct = array();
  1239. foreach ( $posts_list as $post ) {
  1240. $post_type = get_post_type_object( $post['post_type'] );
  1241. if ( ! current_user_can( $post_type->cap->edit_post, $post['ID'] ) )
  1242. continue;
  1243. $struct[] = $this->_prepare_post( $post, $fields );
  1244. }
  1245. return $struct;
  1246. }
  1247. /**
  1248. * Create a new term.
  1249. *
  1250. * @since 3.4.0
  1251. *
  1252. * @uses wp_insert_term()
  1253. * @param array $args Method parameters. Contains:
  1254. * - int $blog_id
  1255. * - string $username
  1256. * - string $password
  1257. * - array $content_struct
  1258. * The $content_struct must contain:
  1259. * - 'name'
  1260. * - 'taxonomy'
  1261. * Also, it can optionally contain:
  1262. * - 'parent'
  1263. * - 'description'
  1264. * - 'slug'
  1265. * @return string term_id
  1266. */
  1267. function wp_newTerm( $args ) {
  1268. if ( ! $this->minimum_args( $args, 4 ) )
  1269. return $this->error;
  1270. $this->escape( $args );
  1271. $blog_id = (int) $args[0];
  1272. $username = $args[1];
  1273. $password = $args[2];
  1274. $content_struct = $args[3];
  1275. if ( ! $user = $this->login( $username, $password ) )
  1276. return $this->error;
  1277. do_action( 'xmlrpc_call', 'wp.newTerm' );
  1278. if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
  1279. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1280. $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
  1281. if ( ! current_user_can( $taxonomy->cap->manage_terms ) )
  1282. return new IXR_Error( 401, __( 'You are not allowed to create terms in this taxonomy.' ) );
  1283. $taxonomy = (array) $taxonomy;
  1284. // hold the data of the term
  1285. $term_data = array();
  1286. $term_data['name'] = trim( $content_struct['name'] );
  1287. if ( empty( $term_data['name'] ) )
  1288. return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
  1289. if ( isset( $content_struct['parent'] ) ) {
  1290. if ( ! $taxonomy['hierarchical'] )
  1291. return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) );
  1292. $parent_term_id = (int) $content_struct['parent'];
  1293. $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
  1294. if ( is_wp_error( $parent_term ) )
  1295. return new IXR_Error( 500, $parent_term->get_error_message() );
  1296. if ( ! $parent_term )
  1297. return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
  1298. $term_data['parent'] = $content_struct['parent'];
  1299. }
  1300. if ( isset( $content_struct['description'] ) )
  1301. $term_data['description'] = $content_struct['description'];
  1302. if ( isset( $content_struct['slug'] ) )
  1303. $term_data['slug'] = $content_struct['slug'];
  1304. $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data );
  1305. if ( is_wp_error( $term ) )
  1306. return new IXR_Error( 500, $term->get_error_message() );
  1307. if ( ! $term )

Large files files are truncated, but you can click here to view the full file