PageRenderTime 70ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 1ms

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

https://github.com/visualzach/belfastguitarfest.com
PHP | 5532 lines | 3399 code | 898 blank | 1235 comment | 603 complexity | e291be52e0a30b82aa5fbf9578d9a4c4 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, GPL-3.0, LGPL-2.1

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

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