PageRenderTime 64ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 1ms

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

https://bitbucket.org/matyhaty/senses-avj-printclub
PHP | 5104 lines | 3123 code | 862 blank | 1119 comment | 568 complexity | 109d98849c0ba6fe7408ddd681688f1a MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.1, GPL-2.0

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. 'link' => post_permalink( $post['ID'] ),
  569. 'comment_status' => $post['comment_status'],
  570. 'ping_status' => $post['ping_status'],
  571. 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
  572. );
  573. // Thumbnail
  574. $post_fields['post_thumbnail'] = array();
  575. $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
  576. if ( $thumbnail_id ) {
  577. $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail';
  578. $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
  579. }
  580. // Consider future posts as published
  581. if ( $post_fields['post_status'] === 'future' )
  582. $post_fields['post_status'] = 'publish';
  583. // Fill in blank post format
  584. $post_fields['post_format'] = get_post_format( $post['ID'] );
  585. if ( empty( $post_fields['post_format'] ) )
  586. $post_fields['post_format'] = 'standard';
  587. // Merge requested $post_fields fields into $_post
  588. if ( in_array( 'post', $fields ) ) {
  589. $_post = array_merge( $_post, $post_fields );
  590. } else {
  591. $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
  592. $_post = array_merge( $_post, $requested_fields );
  593. }
  594. $all_taxonomy_fields = in_array( 'taxonomies', $fields );
  595. if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
  596. $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' );
  597. $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
  598. $_post['terms'] = array();
  599. foreach ( $terms as $term ) {
  600. $_post['terms'][] = $this->_prepare_term( $term );
  601. }
  602. }
  603. if ( in_array( 'custom_fields', $fields ) )
  604. $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] );
  605. if ( in_array( 'enclosure', $fields ) ) {
  606. $_post['enclosure'] = array();
  607. $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' );
  608. if ( ! empty( $enclosures ) ) {
  609. $encdata = explode( "\n", $enclosures[0] );
  610. $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) );
  611. $_post['enclosure']['length'] = (int) trim( $encdata[1] );
  612. $_post['enclosure']['type'] = trim( $encdata[2] );
  613. }
  614. }
  615. return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields );
  616. }
  617. /**
  618. * Prepares post data for return in an XML-RPC object.
  619. *
  620. * @access protected
  621. *
  622. * @param object $post_type Post type object
  623. * @param array $fields The subset of post fields to return
  624. * @return array The prepared post type data
  625. */
  626. protected function _prepare_post_type( $post_type, $fields ) {
  627. $_post_type = array(
  628. 'name' => $post_type->name,
  629. 'label' => $post_type->label,
  630. 'hierarchical' => (bool) $post_type->hierarchical,
  631. 'public' => (bool) $post_type->public,
  632. 'show_ui' => (bool) $post_type->show_ui,
  633. '_builtin' => (bool) $post_type->_builtin,
  634. 'has_archive' => (bool) $post_type->has_archive,
  635. 'supports' => get_all_post_type_supports( $post_type->name ),
  636. );
  637. if ( in_array( 'labels', $fields ) ) {
  638. $_post_type['labels'] = (array) $post_type->labels;
  639. }
  640. if ( in_array( 'cap', $fields ) ) {
  641. $_post_type['cap'] = (array) $post_type->cap;
  642. $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap;
  643. }
  644. if ( in_array( 'menu', $fields ) ) {
  645. $_post_type['menu_position'] = (int) $post_type->menu_position;
  646. $_post_type['menu_icon'] = $post_type->menu_icon;
  647. $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu;
  648. }
  649. if ( in_array( 'taxonomies', $fields ) )
  650. $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' );
  651. return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type );
  652. }
  653. /**
  654. * Prepares media item data for return in an XML-RPC object.
  655. *
  656. * @access protected
  657. *
  658. * @param object $media_item The unprepared media item data
  659. * @param string $thumbnail_size The image size to use for the thumbnail URL
  660. * @return array The prepared media item data
  661. */
  662. protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) {
  663. $_media_item = array(
  664. 'attachment_id' => strval( $media_item->ID ),
  665. 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
  666. 'parent' => $media_item->post_parent,
  667. 'link' => wp_get_attachment_url( $media_item->ID ),
  668. 'title' => $media_item->post_title,
  669. 'caption' => $media_item->post_excerpt,
  670. 'description' => $media_item->post_content,
  671. 'metadata' => wp_get_attachment_metadata( $media_item->ID ),
  672. );
  673. $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
  674. if ( $thumbnail_src )
  675. $_media_item['thumbnail'] = $thumbnail_src[0];
  676. else
  677. $_media_item['thumbnail'] = $_media_item['link'];
  678. return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
  679. }
  680. /**
  681. * Prepares page data for return in an XML-RPC object.
  682. *
  683. * @access protected
  684. *
  685. * @param object $page The unprepared page data
  686. * @return array The prepared page data
  687. */
  688. protected function _prepare_page( $page ) {
  689. // Get all of the page content and link.
  690. $full_page = get_extended( $page->post_content );
  691. $link = post_permalink( $page->ID );
  692. // Get info the page parent if there is one.
  693. $parent_title = "";
  694. if ( ! empty( $page->post_parent ) ) {
  695. $parent = get_page( $page->post_parent );
  696. $parent_title = $parent->post_title;
  697. }
  698. // Determine comment and ping settings.
  699. $allow_comments = comments_open( $page->ID ) ? 1 : 0;
  700. $allow_pings = pings_open( $page->ID ) ? 1 : 0;
  701. // Format page date.
  702. $page_date = $this->_convert_date( $page->post_date );
  703. $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date );
  704. // Pull the categories info together.
  705. $categories = array();
  706. foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) {
  707. $categories[] = get_cat_name( $cat_id );
  708. }
  709. // Get the author info.
  710. $author = get_userdata( $page->post_author );
  711. $page_template = get_page_template_slug( $page->ID );
  712. if ( empty( $page_template ) )
  713. $page_template = 'default';
  714. $_page = array(
  715. 'dateCreated' => $page_date,
  716. 'userid' => $page->post_author,
  717. 'page_id' => $page->ID,
  718. 'page_status' => $page->post_status,
  719. 'description' => $full_page['main'],
  720. 'title' => $page->post_title,
  721. 'link' => $link,
  722. 'permaLink' => $link,
  723. 'categories' => $categories,
  724. 'excerpt' => $page->post_excerpt,
  725. 'text_more' => $full_page['extended'],
  726. 'mt_allow_comments' => $allow_comments,
  727. 'mt_allow_pings' => $allow_pings,
  728. 'wp_slug' => $page->post_name,
  729. 'wp_password' => $page->post_password,
  730. 'wp_author' => $author->display_name,
  731. 'wp_page_parent_id' => $page->post_parent,
  732. 'wp_page_parent_title' => $parent_title,
  733. 'wp_page_order' => $page->menu_order,
  734. 'wp_author_id' => (string) $author->ID,
  735. 'wp_author_display_name' => $author->display_name,
  736. 'date_created_gmt' => $page_date_gmt,
  737. 'custom_fields' => $this->get_custom_fields( $page->ID ),
  738. 'wp_page_template' => $page_template
  739. );
  740. return apply_filters( 'xmlrpc_prepare_page', $_page, $page );
  741. }
  742. /**
  743. * Prepares comment data for return in an XML-RPC object.
  744. *
  745. * @access protected
  746. *
  747. * @param object $comment The unprepared comment data
  748. * @return array The prepared comment data
  749. */
  750. protected function _prepare_comment( $comment ) {
  751. // Format page date.
  752. $comment_date = $this->_convert_date( $comment->comment_date );
  753. $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date );
  754. if ( '0' == $comment->comment_approved )
  755. $comment_status = 'hold';
  756. else if ( 'spam' == $comment->comment_approved )
  757. $comment_status = 'spam';
  758. else if ( '1' == $comment->comment_approved )
  759. $comment_status = 'approve';
  760. else
  761. $comment_status = $comment->comment_approved;
  762. $_comment = array(
  763. 'date_created_gmt' => $comment_date_gmt,
  764. 'user_id' => $comment->user_id,
  765. 'comment_id' => $comment->comment_ID,
  766. 'parent' => $comment->comment_parent,
  767. 'status' => $comment_status,
  768. 'content' => $comment->comment_content,
  769. 'link' => get_comment_link($comment),
  770. 'post_id' => $comment->comment_post_ID,
  771. 'post_title' => get_the_title($comment->comment_post_ID),
  772. 'author' => $comment->comment_author,
  773. 'author_url' => $comment->comment_author_url,
  774. 'author_email' => $comment->comment_author_email,
  775. 'author_ip' => $comment->comment_author_IP,
  776. 'type' => $comment->comment_type,
  777. );
  778. return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment );
  779. }
  780. /**
  781. * Create a new post for any registered post type.
  782. *
  783. * @since 3.4.0
  784. *
  785. * @param array $args Method parameters. Contains:
  786. * - int $blog_id
  787. * - string $username
  788. * - string $password
  789. * - array $content_struct
  790. * $content_struct can contain:
  791. * - post_type (default: 'post')
  792. * - post_status (default: 'draft')
  793. * - post_title
  794. * - post_author
  795. * - post_exerpt
  796. * - post_content
  797. * - post_date_gmt | post_date
  798. * - post_format
  799. * - post_password
  800. * - comment_status - can be 'open' | 'closed'
  801. * - ping_status - can be 'open' | 'closed'
  802. * - sticky
  803. * - post_thumbnail - ID of a media item to use as the post thumbnail/featured image
  804. * - custom_fields - array, with each element containing 'key' and 'value'
  805. * - terms - array, with taxonomy names as keys and arrays of term IDs as values
  806. * - terms_names - array, with taxonomy names as keys and arrays of term names as values
  807. * - enclosure
  808. * - any other fields supported by wp_insert_post()
  809. * @return string post_id
  810. */
  811. function wp_newPost( $args ) {
  812. if ( ! $this->minimum_args( $args, 4 ) )
  813. return $this->error;
  814. $this->escape( $args );
  815. $blog_id = (int) $args[0];
  816. $username = $args[1];
  817. $password = $args[2];
  818. $content_struct = $args[3];
  819. if ( ! $user = $this->login( $username, $password ) )
  820. return $this->error;
  821. do_action( 'xmlrpc_call', 'wp.newPost' );
  822. unset( $content_struct['ID'] );
  823. return $this->_insert_post( $user, $content_struct );
  824. }
  825. /**
  826. * Helper method for filtering out elements from an array.
  827. *
  828. * @since 3.4.0
  829. *
  830. * @param int $count Number to compare to one.
  831. */
  832. private function _is_greater_than_one( $count ) {
  833. return $count > 1;
  834. }
  835. /**
  836. * Helper method for wp_newPost and wp_editPost, containing shared logic.
  837. *
  838. * @since 3.4.0
  839. * @uses wp_insert_post()
  840. *
  841. * @param WP_User $user The post author if post_author isn't set in $content_struct.
  842. * @param array $content_struct Post data to insert.
  843. */
  844. protected function _insert_post( $user, $content_struct ) {
  845. $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0,
  846. 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' );
  847. $post_data = wp_parse_args( $content_struct, $defaults );
  848. $post_type = get_post_type_object( $post_data['post_type'] );
  849. if ( ! $post_type )
  850. return new IXR_Error( 403, __( 'Invalid post type' ) );
  851. $update = ! empty( $post_data['ID'] );
  852. if ( $update ) {
  853. if ( ! get_post( $post_data['ID'] ) )
  854. return new IXR_Error( 401, __( 'Invalid post ID.' ) );
  855. if ( ! current_user_can( $post_type->cap->edit_post, $post_data['ID'] ) )
  856. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
  857. if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) )
  858. return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
  859. } else {
  860. if ( ! current_user_can( $post_type->cap->edit_posts ) )
  861. return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
  862. }
  863. switch ( $post_data['post_status'] ) {
  864. case 'draft':
  865. case 'pending':
  866. break;
  867. case 'private':
  868. if ( ! current_user_can( $post_type->cap->publish_posts ) )
  869. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type' ) );
  870. break;
  871. case 'publish':
  872. case 'future':
  873. if ( ! current_user_can( $post_type->cap->publish_posts ) )
  874. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) );
  875. break;
  876. default:
  877. $post_data['post_status'] = 'draft';
  878. break;
  879. }
  880. if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) )
  881. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type' ) );
  882. $post_data['post_author'] = absint( $post_data['post_author'] );
  883. if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) {
  884. if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
  885. return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) );
  886. $author = get_userdata( $post_data['post_author'] );
  887. if ( ! $author )
  888. return new IXR_Error( 404, __( 'Invalid author ID.' ) );
  889. } else {
  890. $post_data['post_author'] = $user->ID;
  891. }
  892. if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' )
  893. unset( $post_data['comment_status'] );
  894. if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' )
  895. unset( $post_data['ping_status'] );
  896. // Do some timestamp voodoo
  897. if ( ! empty( $post_data['post_date_gmt'] ) ) {
  898. // We know this is supposed to be GMT, so we're going to slap that Z on there by force
  899. $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z';
  900. } elseif ( ! empty( $post_data['post_date'] ) ) {
  901. $dateCreated = $post_data['post_date']->getIso();
  902. }
  903. if ( ! empty( $dateCreated ) ) {
  904. $post_data['post_date'] = get_date_from_gmt( iso8601_to_datetime( $dateCreated ) );
  905. $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'GMT' );
  906. }
  907. if ( ! isset( $post_data['ID'] ) )
  908. $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID;
  909. $post_ID = $post_data['ID'];
  910. if ( $post_data['post_type'] == 'post' ) {
  911. // Private and password-protected posts cannot be stickied.
  912. if ( $post_data['post_status'] == 'private' || ! empty( $post_data['post_password'] ) ) {
  913. // Error if the client tried to stick the post, otherwise, silently unstick.
  914. if ( ! empty( $post_data['sticky'] ) )
  915. return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
  916. if ( $update )
  917. unstick_post( $post_ID );
  918. } elseif ( isset( $post_data['sticky'] ) ) {
  919. if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
  920. return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
  921. if ( $post_data['sticky'] )
  922. stick_post( $post_ID );
  923. else
  924. unstick_post( $post_ID );
  925. }
  926. }
  927. if ( isset( $post_data['post_thumbnail'] ) ) {
  928. // empty value deletes, non-empty value adds/updates
  929. if ( ! $post_data['post_thumbnail'] )
  930. delete_post_thumbnail( $post_ID );
  931. elseif ( ! set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ) )
  932. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
  933. unset( $content_struct['post_thumbnail'] );
  934. }
  935. if ( isset( $post_data['custom_fields'] ) )
  936. $this->set_custom_fields( $post_ID, $post_data['custom_fields'] );
  937. if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) {
  938. $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' );
  939. // accumulate term IDs from terms and terms_names
  940. $terms = array();
  941. // first validate the terms specified by ID
  942. if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) {
  943. $taxonomies = array_keys( $post_data['terms'] );
  944. // validating term ids
  945. foreach ( $taxonomies as $taxonomy ) {
  946. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
  947. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
  948. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
  949. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
  950. $term_ids = $post_data['terms'][$taxonomy];
  951. foreach ( $term_ids as $term_id ) {
  952. $term = get_term_by( 'id', $term_id, $taxonomy );
  953. if ( ! $term )
  954. return new IXR_Error( 403, __( 'Invalid term ID' ) );
  955. $terms[$taxonomy][] = (int) $term_id;
  956. }
  957. }
  958. }
  959. // now validate terms specified by name
  960. if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) {
  961. $taxonomies = array_keys( $post_data['terms_names'] );
  962. foreach ( $taxonomies as $taxonomy ) {
  963. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
  964. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
  965. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
  966. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
  967. // for hierarchical taxonomies, we can't assign a term when multiple terms in the hierarchy share the same name
  968. $ambiguous_terms = array();
  969. if ( is_taxonomy_hierarchical( $taxonomy ) ) {
  970. $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) );
  971. // count the number of terms with the same name
  972. $tax_term_names_count = array_count_values( $tax_term_names );
  973. // filter out non-ambiguous term names
  974. $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') );
  975. $ambiguous_terms = array_keys( $ambiguous_tax_term_counts );
  976. }
  977. $term_names = $post_data['terms_names'][$taxonomy];
  978. foreach ( $term_names as $term_name ) {
  979. if ( in_array( $term_name, $ambiguous_terms ) )
  980. return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) );
  981. $term = get_term_by( 'name', $term_name, $taxonomy );
  982. if ( ! $term ) {
  983. // term doesn't exist, so check that the user is allowed to create new terms
  984. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) )
  985. return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) );
  986. // create the new term
  987. $term_info = wp_insert_term( $term_name, $taxonomy );
  988. if ( is_wp_error( $term_info ) )
  989. return new IXR_Error( 500, $term_info->get_error_message() );
  990. $terms[$taxonomy][] = (int) $term_info['term_id'];
  991. } else {
  992. $terms[$taxonomy][] = (int) $term->term_id;
  993. }
  994. }
  995. }
  996. }
  997. $post_data['tax_input'] = $terms;
  998. unset( $post_data['terms'], $post_data['terms_names'] );
  999. } else {
  1000. // do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names'
  1001. unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] );
  1002. }
  1003. if ( isset( $post_data['post_format'] ) ) {
  1004. $format = set_post_format( $post_ID, $post_data['post_format'] );
  1005. if ( is_wp_error( $format ) )
  1006. return new IXR_Error( 500, $format->get_error_message() );
  1007. unset( $post_data['post_format'] );
  1008. }
  1009. // Handle enclosures
  1010. $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null;
  1011. $this->add_enclosure_if_new( $post_ID, $enclosure );
  1012. $this->attach_uploads( $post_ID, $post_data['post_content'] );
  1013. $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
  1014. $post_ID = wp_insert_post( $post_data, true );
  1015. if ( is_wp_error( $post_ID ) )
  1016. return new IXR_Error( 500, $post_ID->get_error_message() );
  1017. if ( ! $post_ID )
  1018. return new IXR_Error( 401, __( 'Sorry, your entry could not be posted. Something wrong happened.' ) );
  1019. return strval( $post_ID );
  1020. }
  1021. /**
  1022. * Edit a post for any registered post type.
  1023. *
  1024. * The $content_struct parameter only needs to contain fields that
  1025. * should be changed. All other fields will retain their existing values.
  1026. *
  1027. * @since 3.4.0
  1028. *
  1029. * @param array $args Method parameters. Contains:
  1030. * - int $blog_id
  1031. * - string $username
  1032. * - string $password
  1033. * - int $post_id
  1034. * - array $content_struct
  1035. * @return true on success
  1036. */
  1037. function wp_editPost( $args ) {
  1038. if ( ! $this->minimum_args( $args, 5 ) )
  1039. return $this->error;
  1040. $this->escape( $args );
  1041. $blog_id = (int) $args[0];
  1042. $username = $args[1];
  1043. $password = $args[2];
  1044. $post_id = (int) $args[3];
  1045. $content_struct = $args[4];
  1046. if ( ! $user = $this->login( $username, $password ) )
  1047. return $this->error;
  1048. do_action( 'xmlrpc_call', 'wp.editPost' );
  1049. $post = get_post( $post_id, ARRAY_A );
  1050. if ( empty( $post['ID'] ) )
  1051. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1052. // convert the date field back to IXR form
  1053. $post['post_date'] = $this->_convert_date( $post['post_date'] );
  1054. // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
  1055. // since _insert_post will ignore the non-GMT date if the GMT date is set
  1056. if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) )
  1057. unset( $post['post_date_gmt'] );
  1058. else
  1059. $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] );
  1060. $this->escape( $post );
  1061. $merged_content_struct = array_merge( $post, $content_struct );
  1062. $retval = $this->_insert_post( $user, $merged_content_struct );
  1063. if ( $retval instanceof IXR_Error )
  1064. return $retval;
  1065. return true;
  1066. }
  1067. /**
  1068. * Delete a post for any registered post type.
  1069. *
  1070. * @since 3.4.0
  1071. *
  1072. * @uses wp_delete_post()
  1073. * @param array $args Method parameters. Contains:
  1074. * - int $blog_id
  1075. * - string $username
  1076. * - string $password
  1077. * - int $post_id
  1078. * @return true on success
  1079. */
  1080. function wp_deletePost( $args ) {
  1081. if ( ! $this->minimum_args( $args, 4 ) )
  1082. return $this->error;
  1083. $this->escape( $args );
  1084. $blog_id = (int) $args[0];
  1085. $username = $args[1];
  1086. $password = $args[2];
  1087. $post_id = (int) $args[3];
  1088. if ( ! $user = $this->login( $username, $password ) )
  1089. return $this->error;
  1090. do_action( 'xmlrpc_call', 'wp.deletePost' );
  1091. $post = wp_get_single_post( $post_id, ARRAY_A );
  1092. if ( empty( $post['ID'] ) )
  1093. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1094. $post_type = get_post_type_object( $post['post_type'] );
  1095. if ( ! current_user_can( $post_type->cap->delete_post, $post_id ) )
  1096. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) );
  1097. $result = wp_delete_post( $post_id );
  1098. if ( ! $result )
  1099. return new IXR_Error( 500, __( 'The post cannot be deleted.' ) );
  1100. return true;
  1101. }
  1102. /**
  1103. * Retrieve a post.
  1104. *
  1105. * @since 3.4.0
  1106. *
  1107. * The optional $fields parameter specifies what fields will be included
  1108. * in the response array. This should be a list of field names. 'post_id' will
  1109. * always be included in the response regardless of the value of $fields.
  1110. *
  1111. * Instead of, or in addition to, individual field names, conceptual group
  1112. * names can be used to specify multiple fields. The available conceptual
  1113. * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
  1114. * and 'enclosure'.
  1115. *
  1116. * @uses wp_get_single_post()
  1117. * @param array $args Method parameters. Contains:
  1118. * - int $post_id
  1119. * - string $username
  1120. * - string $password
  1121. * - array $fields optional
  1122. * @return array contains (based on $fields parameter):
  1123. * - 'post_id'
  1124. * - 'post_title'
  1125. * - 'post_date'
  1126. * - 'post_date_gmt'
  1127. * - 'post_modified'
  1128. * - 'post_modified_gmt'
  1129. * - 'post_status'
  1130. * - 'post_type'
  1131. * - 'post_name'
  1132. * - 'post_author'
  1133. * - 'post_password'
  1134. * - 'post_excerpt'
  1135. * - 'post_content'
  1136. * - 'link'
  1137. * - 'comment_status'
  1138. * - 'ping_status'
  1139. * - 'sticky'
  1140. * - 'custom_fields'
  1141. * - 'terms'
  1142. * - 'categories'
  1143. * - 'tags'
  1144. * - 'enclosure'
  1145. */
  1146. function wp_getPost( $args ) {
  1147. if ( ! $this->minimum_args( $args, 4 ) )
  1148. return $this->error;
  1149. $this->escape( $args );
  1150. $blog_id = (int) $args[0];
  1151. $username = $args[1];
  1152. $password = $args[2];
  1153. $post_id = (int) $args[3];
  1154. if ( isset( $args[4] ) )
  1155. $fields = $args[4];
  1156. else
  1157. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' );
  1158. if ( ! $user = $this->login( $username, $password ) )
  1159. return $this->error;
  1160. do_action( 'xmlrpc_call', 'wp.getPost' );
  1161. $post = wp_get_single_post( $post_id, ARRAY_A );
  1162. if ( empty( $post['ID'] ) )
  1163. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1164. $post_type = get_post_type_object( $post['post_type'] );
  1165. if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) )
  1166. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  1167. return $this->_prepare_post( $post, $fields );
  1168. }
  1169. /**
  1170. * Retrieve posts.
  1171. *
  1172. * @since 3.4.0
  1173. *
  1174. * The optional $filter parameter modifies the query used to retrieve posts.
  1175. * Accepted keys are 'post_type', 'post_status', 'number', 'offset',
  1176. * 'orderby', and 'order'.
  1177. *
  1178. * The optional $fields parameter specifies what fields will be included
  1179. * in the response array.
  1180. *
  1181. * @uses wp_get_recent_posts()
  1182. * @see wp_getPost() for more on $fields
  1183. * @see get_posts() for more on $filter values
  1184. *
  1185. * @param array $args Method parameters. Contains:
  1186. * - int $blog_id
  1187. * - string $username
  1188. * - string $password
  1189. * - array $filter optional
  1190. * - array $fields optional
  1191. * @return array contains a collection of posts.
  1192. */
  1193. function wp_getPosts( $args ) {
  1194. if ( ! $this->minimum_args( $args, 3 ) )
  1195. return $this->error;
  1196. $this->escape( $args );
  1197. $blog_id = (int) $args[0];
  1198. $username = $args[1];
  1199. $password = $args[2];
  1200. $filter = isset( $args[3] ) ? $args[3] : array();
  1201. if ( isset( $args[4] ) )
  1202. $fields = $args[4];
  1203. else
  1204. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' );
  1205. if ( ! $user = $this->login( $username, $password ) )
  1206. return $this->error;
  1207. do_action( 'xmlrpc_call', 'wp.getPosts' );
  1208. $query = array();
  1209. if ( isset( $filter['post_type'] ) ) {
  1210. $post_type = get_post_type_object( $filter['post_type'] );
  1211. if ( ! ( (bool) $post_type ) )
  1212. return new IXR_Error( 403, __( 'The post type specified is not valid' ) );
  1213. } else {
  1214. $post_type = get_post_type_object( 'post' );
  1215. }
  1216. if ( ! current_user_can( $post_type->cap->edit_posts ) )
  1217. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type' ));
  1218. $query['post_type'] = $post_type->name;
  1219. if ( isset( $filter['post_status'] ) )
  1220. $query['post_status'] = $filter['post_status'];
  1221. if ( isset( $filter['number'] ) )
  1222. $query['numberposts'] = absint( $filter['number'] );
  1223. if ( isset( $filter['offset'] ) )
  1224. $query['offset'] = absint( $filter['offset'] );
  1225. if ( isset( $filter['orderby'] ) ) {
  1226. $query['orderby'] = $filter['orderby'];
  1227. if ( isset( $filter['order'] ) )
  1228. $query['order'] = $filter['order'];
  1229. }
  1230. $posts_list = wp_get_recent_posts( $query );
  1231. if ( ! $posts_list )
  1232. return array();
  1233. // holds all the posts data
  1234. $struct = array();
  1235. foreach ( $posts_list as $post ) {
  1236. $post_type = get_post_type_object( $post['post_type'] );
  1237. if ( ! current_user_can( $post_type->cap->edit_post, $post['ID'] ) )
  1238. continue;
  1239. $struct[] = $this->_prepare_post( $post, $fields );
  1240. }
  1241. return $struct;
  1242. }
  1243. /**
  1244. * Create a new term.
  1245. *
  1246. * @since 3.4.0
  1247. *
  1248. * @uses wp_insert_term()
  1249. * @param array $args Method parameters. Contains:
  1250. * - int $blog_id
  1251. * - string $username
  1252. * - string $password
  1253. * - array $content_struct
  1254. * The $content_struct must contain:
  1255. * - 'name'
  1256. * - 'taxonomy'
  1257. * Also, it can optionally contain:
  1258. * - 'parent'
  1259. * - 'description'
  1260. * - 'slug'
  1261. * @return string term_id
  1262. */
  1263. function wp_newTerm( $args ) {
  1264. if ( ! $this->minimum_args( $args, 4 ) )
  1265. return $this->error;
  1266. $this->escape( $args );
  1267. $blog_id = (int) $args[0];
  1268. $username = $args[1];
  1269. $password = $args[2];
  1270. $content_struct = $args[3];
  1271. if ( ! $user = $this->login( $username, $password ) )
  1272. return $this->error;
  1273. do_action( 'xmlrpc_call', 'wp.newTerm' );
  1274. if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
  1275. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1276. $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
  1277. if ( ! current_user_can( $taxonomy->cap->manage_terms ) )
  1278. return new IXR_Error( 401, __( 'You are not allowed to create terms in this taxonomy.' ) );
  1279. $taxonomy = (array) $taxonomy;
  1280. // hold the data of the term
  1281. $term_data = array();
  1282. $term_data['name'] = trim( $content_struct['name'] );
  1283. if ( empty( $term_data['name'] ) )
  1284. return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
  1285. if ( isset( $content_struct['parent'] ) ) {
  1286. if ( ! $taxonomy['hierarchical'] )
  1287. return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) );
  1288. $parent_term_id = (int) $content_struct['parent'];
  1289. $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
  1290. if ( is_wp_error( $parent_term ) )
  1291. return new IXR_Error( 500, $parent_term->get_error_message() );
  1292. if ( ! $parent_term )
  1293. return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
  1294. $term_data['parent'] = $content_struct['parent'];
  1295. }
  1296. if ( isset( $content_struct['description'] ) )
  1297. $term_data['description'] = $content_struct['description'];
  1298. if ( isset( $content_struct['slug'] ) )
  1299. $term_data['slug'] = $content_struct['slug'];
  1300. $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data );
  1301. if ( is_wp_error( $term ) )
  1302. return new IXR_Error( 500, $term->get_error_message() );
  1303. if ( ! $term )
  1304. return new IXR_Error( 500, __( 'Sorry, your term could not be created. Something wrong happened.' ) );
  1305. return strval( $term['term_id'] );
  1306. }
  1307. /**
  1308. * Edit a term.
  1309. *
  1310. * @since 3.4.0
  1311. *
  1312. * @uses wp_u…

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