PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/dkrzos/phc
PHP | 5508 lines | 3379 code | 894 blank | 1235 comment | 606 complexity | 5a2508e11a04b0c41b70a2ad87d9e6b7 MD5 | raw file
Possible License(s): GPL-2.0
  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.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. * Sanitize string or array of strings for database.
  195. *
  196. * @since 1.5.2
  197. *
  198. * @param string|array $array Sanitize single string or array of strings.
  199. * @return string|array Type matches $array and sanitized for the database.
  200. */
  201. function escape(&$array) {
  202. global $wpdb;
  203. if (!is_array($array)) {
  204. return($wpdb->escape($array));
  205. } else {
  206. foreach ( (array) $array as $k => $v ) {
  207. if ( is_array($v) ) {
  208. $this->escape($array[$k]);
  209. } else if ( is_object($v) ) {
  210. //skip
  211. } else {
  212. $array[$k] = $wpdb->escape($v);
  213. }
  214. }
  215. }
  216. }
  217. /**
  218. * Retrieve custom fields for post.
  219. *
  220. * @since 2.5.0
  221. *
  222. * @param int $post_id Post ID.
  223. * @return array Custom fields, if exist.
  224. */
  225. function get_custom_fields($post_id) {
  226. $post_id = (int) $post_id;
  227. $custom_fields = array();
  228. foreach ( (array) has_meta($post_id) as $meta ) {
  229. // Don't expose protected fields.
  230. if ( ! current_user_can( 'edit_post_meta', $post_id , $meta['meta_key'] ) )
  231. continue;
  232. $custom_fields[] = array(
  233. "id" => $meta['meta_id'],
  234. "key" => $meta['meta_key'],
  235. "value" => $meta['meta_value']
  236. );
  237. }
  238. return $custom_fields;
  239. }
  240. /**
  241. * Set custom fields for post.
  242. *
  243. * @since 2.5.0
  244. *
  245. * @param int $post_id Post ID.
  246. * @param array $fields Custom fields.
  247. */
  248. function set_custom_fields($post_id, $fields) {
  249. $post_id = (int) $post_id;
  250. foreach ( (array) $fields as $meta ) {
  251. if ( isset($meta['id']) ) {
  252. $meta['id'] = (int) $meta['id'];
  253. $pmeta = get_metadata_by_mid( 'post', $meta['id'] );
  254. if ( isset($meta['key']) ) {
  255. $meta['key'] = stripslashes( $meta['key'] );
  256. if ( $meta['key'] != $pmeta->meta_key )
  257. continue;
  258. $meta['value'] = stripslashes_deep( $meta['value'] );
  259. if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) )
  260. update_metadata_by_mid( 'post', $meta['id'], $meta['value'] );
  261. } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) {
  262. delete_metadata_by_mid( 'post', $meta['id'] );
  263. }
  264. } elseif ( current_user_can( 'add_post_meta', $post_id, stripslashes( $meta['key'] ) ) ) {
  265. add_post_meta( $post_id, $meta['key'], $meta['value'] );
  266. }
  267. }
  268. }
  269. /**
  270. * Set up blog options property.
  271. *
  272. * Passes property through 'xmlrpc_blog_options' filter.
  273. *
  274. * @since 2.6.0
  275. */
  276. function initialise_blog_option_info() {
  277. global $wp_version;
  278. $this->blog_options = array(
  279. // Read only options
  280. 'software_name' => array(
  281. 'desc' => __( 'Software Name' ),
  282. 'readonly' => true,
  283. 'value' => 'WordPress'
  284. ),
  285. 'software_version' => array(
  286. 'desc' => __( 'Software Version' ),
  287. 'readonly' => true,
  288. 'value' => $wp_version
  289. ),
  290. 'blog_url' => array(
  291. 'desc' => __( 'Site URL' ),
  292. 'readonly' => true,
  293. 'option' => 'siteurl'
  294. ),
  295. 'home_url' => array(
  296. 'desc' => __( 'Home URL' ),
  297. 'readonly' => true,
  298. 'option' => 'home'
  299. ),
  300. 'image_default_link_type' => array(
  301. 'desc' => __( 'Image default link type' ),
  302. 'readonly' => true,
  303. 'option' => 'image_default_link_type'
  304. ),
  305. 'image_default_size' => array(
  306. 'desc' => __( 'Image default size' ),
  307. 'readonly' => true,
  308. 'option' => 'image_default_size'
  309. ),
  310. 'image_default_align' => array(
  311. 'desc' => __( 'Image default align' ),
  312. 'readonly' => true,
  313. 'option' => 'image_default_align'
  314. ),
  315. 'template' => array(
  316. 'desc' => __( 'Template' ),
  317. 'readonly' => true,
  318. 'option' => 'template'
  319. ),
  320. 'stylesheet' => array(
  321. 'desc' => __( 'Stylesheet' ),
  322. 'readonly' => true,
  323. 'option' => 'stylesheet'
  324. ),
  325. 'post_thumbnail' => array(
  326. 'desc' => __('Post Thumbnail'),
  327. 'readonly' => true,
  328. 'value' => current_theme_supports( 'post-thumbnails' )
  329. ),
  330. // Updatable options
  331. 'time_zone' => array(
  332. 'desc' => __( 'Time Zone' ),
  333. 'readonly' => false,
  334. 'option' => 'gmt_offset'
  335. ),
  336. 'blog_title' => array(
  337. 'desc' => __( 'Site Title' ),
  338. 'readonly' => false,
  339. 'option' => 'blogname'
  340. ),
  341. 'blog_tagline' => array(
  342. 'desc' => __( 'Site Tagline' ),
  343. 'readonly' => false,
  344. 'option' => 'blogdescription'
  345. ),
  346. 'date_format' => array(
  347. 'desc' => __( 'Date Format' ),
  348. 'readonly' => false,
  349. 'option' => 'date_format'
  350. ),
  351. 'time_format' => array(
  352. 'desc' => __( 'Time Format' ),
  353. 'readonly' => false,
  354. 'option' => 'time_format'
  355. ),
  356. 'users_can_register' => array(
  357. 'desc' => __( 'Allow new users to sign up' ),
  358. 'readonly' => false,
  359. 'option' => 'users_can_register'
  360. ),
  361. 'thumbnail_size_w' => array(
  362. 'desc' => __( 'Thumbnail Width' ),
  363. 'readonly' => false,
  364. 'option' => 'thumbnail_size_w'
  365. ),
  366. 'thumbnail_size_h' => array(
  367. 'desc' => __( 'Thumbnail Height' ),
  368. 'readonly' => false,
  369. 'option' => 'thumbnail_size_h'
  370. ),
  371. 'thumbnail_crop' => array(
  372. 'desc' => __( 'Crop thumbnail to exact dimensions' ),
  373. 'readonly' => false,
  374. 'option' => 'thumbnail_crop'
  375. ),
  376. 'medium_size_w' => array(
  377. 'desc' => __( 'Medium size image width' ),
  378. 'readonly' => false,
  379. 'option' => 'medium_size_w'
  380. ),
  381. 'medium_size_h' => array(
  382. 'desc' => __( 'Medium size image height' ),
  383. 'readonly' => false,
  384. 'option' => 'medium_size_h'
  385. ),
  386. 'large_size_w' => array(
  387. 'desc' => __( 'Large size image width' ),
  388. 'readonly' => false,
  389. 'option' => 'large_size_w'
  390. ),
  391. 'large_size_h' => array(
  392. 'desc' => __( 'Large size image height' ),
  393. 'readonly' => false,
  394. 'option' => 'large_size_h'
  395. ),
  396. 'default_comment_status' => array(
  397. 'desc' => __( 'Allow people to post comments on new articles' ),
  398. 'readonly' => false,
  399. 'option' => 'default_comment_status'
  400. ),
  401. 'default_ping_status' => array(
  402. 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks)' ),
  403. 'readonly' => false,
  404. 'option' => 'default_ping_status'
  405. )
  406. );
  407. $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
  408. }
  409. /**
  410. * Retrieve the blogs of the user.
  411. *
  412. * @since 2.6.0
  413. *
  414. * @param array $args Method parameters. Contains:
  415. * - username
  416. * - password
  417. * @return array. Contains:
  418. * - 'isAdmin'
  419. * - 'url'
  420. * - 'blogid'
  421. * - 'blogName'
  422. * - 'xmlrpc' - url of xmlrpc endpoint
  423. */
  424. function wp_getUsersBlogs( $args ) {
  425. global $current_site;
  426. // If this isn't on WPMU then just use blogger_getUsersBlogs
  427. if ( !is_multisite() ) {
  428. array_unshift( $args, 1 );
  429. return $this->blogger_getUsersBlogs( $args );
  430. }
  431. $this->escape( $args );
  432. $username = $args[0];
  433. $password = $args[1];
  434. if ( !$user = $this->login($username, $password) )
  435. return $this->error;
  436. do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
  437. $blogs = (array) get_blogs_of_user( $user->ID );
  438. $struct = array();
  439. foreach ( $blogs as $blog ) {
  440. // Don't include blogs that aren't hosted at this site
  441. if ( $blog->site_id != $current_site->id )
  442. continue;
  443. $blog_id = $blog->userblog_id;
  444. switch_to_blog( $blog_id );
  445. $is_admin = current_user_can( 'manage_options' );
  446. $struct[] = array(
  447. 'isAdmin' => $is_admin,
  448. 'url' => home_url( '/' ),
  449. 'blogid' => (string) $blog_id,
  450. 'blogName' => get_option( 'blogname' ),
  451. 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),
  452. );
  453. restore_current_blog();
  454. }
  455. return $struct;
  456. }
  457. /**
  458. * Checks if the method received at least the minimum number of arguments.
  459. *
  460. * @since 3.4.0
  461. *
  462. * @param string|array $args Sanitize single string or array of strings.
  463. * @param int $count Minimum number of arguments.
  464. * @return boolean if $args contains at least $count arguments.
  465. */
  466. protected function minimum_args( $args, $count ) {
  467. if ( count( $args ) < $count ) {
  468. $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) );
  469. return false;
  470. }
  471. return true;
  472. }
  473. /**
  474. * Prepares taxonomy data for return in an XML-RPC object.
  475. *
  476. * @access protected
  477. *
  478. * @param object $taxonomy The unprepared taxonomy data
  479. * @param array $fields The subset of taxonomy fields to return
  480. * @return array The prepared taxonomy data
  481. */
  482. protected function _prepare_taxonomy( $taxonomy, $fields ) {
  483. $_taxonomy = array(
  484. 'name' => $taxonomy->name,
  485. 'label' => $taxonomy->label,
  486. 'hierarchical' => (bool) $taxonomy->hierarchical,
  487. 'public' => (bool) $taxonomy->public,
  488. 'show_ui' => (bool) $taxonomy->show_ui,
  489. '_builtin' => (bool) $taxonomy->_builtin,
  490. );
  491. if ( in_array( 'labels', $fields ) )
  492. $_taxonomy['labels'] = (array) $taxonomy->labels;
  493. if ( in_array( 'cap', $fields ) )
  494. $_taxonomy['cap'] = (array) $taxonomy->cap;
  495. if ( in_array( 'object_type', $fields ) )
  496. $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type );
  497. return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields );
  498. }
  499. /**
  500. * Prepares term data for return in an XML-RPC object.
  501. *
  502. * @access protected
  503. *
  504. * @param array|object $term The unprepared term data
  505. * @return array The prepared term data
  506. */
  507. protected function _prepare_term( $term ) {
  508. $_term = $term;
  509. if ( ! is_array( $_term) )
  510. $_term = get_object_vars( $_term );
  511. // For Intergers which may be largeer than XMLRPC supports ensure we return strings.
  512. $_term['term_id'] = strval( $_term['term_id'] );
  513. $_term['term_group'] = strval( $_term['term_group'] );
  514. $_term['term_taxonomy_id'] = strval( $_term['term_taxonomy_id'] );
  515. $_term['parent'] = strval( $_term['parent'] );
  516. // Count we are happy to return as an Integer because people really shouldn't use Terms that much.
  517. $_term['count'] = intval( $_term['count'] );
  518. return apply_filters( 'xmlrpc_prepare_term', $_term, $term );
  519. }
  520. /**
  521. * Convert a WordPress date string to an IXR_Date object.
  522. *
  523. * @access protected
  524. *
  525. * @param string $date
  526. * @return IXR_Date
  527. */
  528. protected function _convert_date( $date ) {
  529. if ( $date === '0000-00-00 00:00:00' ) {
  530. return new IXR_Date( '00000000T00:00:00Z' );
  531. }
  532. return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) );
  533. }
  534. /**
  535. * Convert a WordPress GMT date string to an IXR_Date object.
  536. *
  537. * @access protected
  538. *
  539. * @param string $date_gmt
  540. * @param string $date
  541. * @return IXR_Date
  542. */
  543. protected function _convert_date_gmt( $date_gmt, $date ) {
  544. if ( $date !== '0000-00-00 00:00:00' && $date_gmt === '0000-00-00 00:00:00' ) {
  545. return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) );
  546. }
  547. return $this->_convert_date( $date_gmt );
  548. }
  549. /**
  550. * Prepares post data for return in an XML-RPC object.
  551. *
  552. * @access protected
  553. *
  554. * @param array $post The unprepared post data
  555. * @param array $fields The subset of post type fields to return
  556. * @return array The prepared post data
  557. */
  558. protected function _prepare_post( $post, $fields ) {
  559. // holds the data for this post. built up based on $fields
  560. $_post = array( 'post_id' => strval( $post['ID'] ) );
  561. // prepare common post fields
  562. $post_fields = array(
  563. 'post_title' => $post['post_title'],
  564. 'post_date' => $this->_convert_date( $post['post_date'] ),
  565. 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ),
  566. 'post_modified' => $this->_convert_date( $post['post_modified'] ),
  567. 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ),
  568. 'post_status' => $post['post_status'],
  569. 'post_type' => $post['post_type'],
  570. 'post_name' => $post['post_name'],
  571. 'post_author' => $post['post_author'],
  572. 'post_password' => $post['post_password'],
  573. 'post_excerpt' => $post['post_excerpt'],
  574. 'post_content' => $post['post_content'],
  575. 'post_parent' => strval( $post['post_parent'] ),
  576. 'post_mime_type' => $post['post_mime_type'],
  577. 'link' => post_permalink( $post['ID'] ),
  578. 'guid' => $post['guid'],
  579. 'menu_order' => intval( $post['menu_order'] ),
  580. 'comment_status' => $post['comment_status'],
  581. 'ping_status' => $post['ping_status'],
  582. 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
  583. );
  584. // Thumbnail
  585. $post_fields['post_thumbnail'] = array();
  586. $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
  587. if ( $thumbnail_id ) {
  588. $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail';
  589. $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
  590. }
  591. // Consider future posts as published
  592. if ( $post_fields['post_status'] === 'future' )
  593. $post_fields['post_status'] = 'publish';
  594. // Fill in blank post format
  595. $post_fields['post_format'] = get_post_format( $post['ID'] );
  596. if ( empty( $post_fields['post_format'] ) )
  597. $post_fields['post_format'] = 'standard';
  598. // Merge requested $post_fields fields into $_post
  599. if ( in_array( 'post', $fields ) ) {
  600. $_post = array_merge( $_post, $post_fields );
  601. } else {
  602. $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
  603. $_post = array_merge( $_post, $requested_fields );
  604. }
  605. $all_taxonomy_fields = in_array( 'taxonomies', $fields );
  606. if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
  607. $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' );
  608. $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
  609. $_post['terms'] = array();
  610. foreach ( $terms as $term ) {
  611. $_post['terms'][] = $this->_prepare_term( $term );
  612. }
  613. }
  614. if ( in_array( 'custom_fields', $fields ) )
  615. $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] );
  616. if ( in_array( 'enclosure', $fields ) ) {
  617. $_post['enclosure'] = array();
  618. $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' );
  619. if ( ! empty( $enclosures ) ) {
  620. $encdata = explode( "\n", $enclosures[0] );
  621. $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) );
  622. $_post['enclosure']['length'] = (int) trim( $encdata[1] );
  623. $_post['enclosure']['type'] = trim( $encdata[2] );
  624. }
  625. }
  626. return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields );
  627. }
  628. /**
  629. * Prepares post data for return in an XML-RPC object.
  630. *
  631. * @access protected
  632. *
  633. * @param object $post_type Post type object
  634. * @param array $fields The subset of post fields to return
  635. * @return array The prepared post type data
  636. */
  637. protected function _prepare_post_type( $post_type, $fields ) {
  638. $_post_type = array(
  639. 'name' => $post_type->name,
  640. 'label' => $post_type->label,
  641. 'hierarchical' => (bool) $post_type->hierarchical,
  642. 'public' => (bool) $post_type->public,
  643. 'show_ui' => (bool) $post_type->show_ui,
  644. '_builtin' => (bool) $post_type->_builtin,
  645. 'has_archive' => (bool) $post_type->has_archive,
  646. 'supports' => get_all_post_type_supports( $post_type->name ),
  647. );
  648. if ( in_array( 'labels', $fields ) ) {
  649. $_post_type['labels'] = (array) $post_type->labels;
  650. }
  651. if ( in_array( 'cap', $fields ) ) {
  652. $_post_type['cap'] = (array) $post_type->cap;
  653. $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap;
  654. }
  655. if ( in_array( 'menu', $fields ) ) {
  656. $_post_type['menu_position'] = (int) $post_type->menu_position;
  657. $_post_type['menu_icon'] = $post_type->menu_icon;
  658. $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu;
  659. }
  660. if ( in_array( 'taxonomies', $fields ) )
  661. $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' );
  662. return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type );
  663. }
  664. /**
  665. * Prepares media item data for return in an XML-RPC object.
  666. *
  667. * @access protected
  668. *
  669. * @param object $media_item The unprepared media item data
  670. * @param string $thumbnail_size The image size to use for the thumbnail URL
  671. * @return array The prepared media item data
  672. */
  673. protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) {
  674. $_media_item = array(
  675. 'attachment_id' => strval( $media_item->ID ),
  676. 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
  677. 'parent' => $media_item->post_parent,
  678. 'link' => wp_get_attachment_url( $media_item->ID ),
  679. 'title' => $media_item->post_title,
  680. 'caption' => $media_item->post_excerpt,
  681. 'description' => $media_item->post_content,
  682. 'metadata' => wp_get_attachment_metadata( $media_item->ID ),
  683. );
  684. $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
  685. if ( $thumbnail_src )
  686. $_media_item['thumbnail'] = $thumbnail_src[0];
  687. else
  688. $_media_item['thumbnail'] = $_media_item['link'];
  689. return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
  690. }
  691. /**
  692. * Prepares page data for return in an XML-RPC object.
  693. *
  694. * @access protected
  695. *
  696. * @param object $page The unprepared page data
  697. * @return array The prepared page data
  698. */
  699. protected function _prepare_page( $page ) {
  700. // Get all of the page content and link.
  701. $full_page = get_extended( $page->post_content );
  702. $link = post_permalink( $page->ID );
  703. // Get info the page parent if there is one.
  704. $parent_title = "";
  705. if ( ! empty( $page->post_parent ) ) {
  706. $parent = get_post( $page->post_parent );
  707. $parent_title = $parent->post_title;
  708. }
  709. // Determine comment and ping settings.
  710. $allow_comments = comments_open( $page->ID ) ? 1 : 0;
  711. $allow_pings = pings_open( $page->ID ) ? 1 : 0;
  712. // Format page date.
  713. $page_date = $this->_convert_date( $page->post_date );
  714. $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date );
  715. // Pull the categories info together.
  716. $categories = array();
  717. foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) {
  718. $categories[] = get_cat_name( $cat_id );
  719. }
  720. // Get the author info.
  721. $author = get_userdata( $page->post_author );
  722. $page_template = get_page_template_slug( $page->ID );
  723. if ( empty( $page_template ) )
  724. $page_template = 'default';
  725. $_page = array(
  726. 'dateCreated' => $page_date,
  727. 'userid' => $page->post_author,
  728. 'page_id' => $page->ID,
  729. 'page_status' => $page->post_status,
  730. 'description' => $full_page['main'],
  731. 'title' => $page->post_title,
  732. 'link' => $link,
  733. 'permaLink' => $link,
  734. 'categories' => $categories,
  735. 'excerpt' => $page->post_excerpt,
  736. 'text_more' => $full_page['extended'],
  737. 'mt_allow_comments' => $allow_comments,
  738. 'mt_allow_pings' => $allow_pings,
  739. 'wp_slug' => $page->post_name,
  740. 'wp_password' => $page->post_password,
  741. 'wp_author' => $author->display_name,
  742. 'wp_page_parent_id' => $page->post_parent,
  743. 'wp_page_parent_title' => $parent_title,
  744. 'wp_page_order' => $page->menu_order,
  745. 'wp_author_id' => (string) $author->ID,
  746. 'wp_author_display_name' => $author->display_name,
  747. 'date_created_gmt' => $page_date_gmt,
  748. 'custom_fields' => $this->get_custom_fields( $page->ID ),
  749. 'wp_page_template' => $page_template
  750. );
  751. return apply_filters( 'xmlrpc_prepare_page', $_page, $page );
  752. }
  753. /**
  754. * Prepares comment data for return in an XML-RPC object.
  755. *
  756. * @access protected
  757. *
  758. * @param object $comment The unprepared comment data
  759. * @return array The prepared comment data
  760. */
  761. protected function _prepare_comment( $comment ) {
  762. // Format page date.
  763. $comment_date = $this->_convert_date( $comment->comment_date );
  764. $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date );
  765. if ( '0' == $comment->comment_approved )
  766. $comment_status = 'hold';
  767. else if ( 'spam' == $comment->comment_approved )
  768. $comment_status = 'spam';
  769. else if ( '1' == $comment->comment_approved )
  770. $comment_status = 'approve';
  771. else
  772. $comment_status = $comment->comment_approved;
  773. $_comment = array(
  774. 'date_created_gmt' => $comment_date_gmt,
  775. 'user_id' => $comment->user_id,
  776. 'comment_id' => $comment->comment_ID,
  777. 'parent' => $comment->comment_parent,
  778. 'status' => $comment_status,
  779. 'content' => $comment->comment_content,
  780. 'link' => get_comment_link($comment),
  781. 'post_id' => $comment->comment_post_ID,
  782. 'post_title' => get_the_title($comment->comment_post_ID),
  783. 'author' => $comment->comment_author,
  784. 'author_url' => $comment->comment_author_url,
  785. 'author_email' => $comment->comment_author_email,
  786. 'author_ip' => $comment->comment_author_IP,
  787. 'type' => $comment->comment_type,
  788. );
  789. return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment );
  790. }
  791. /**
  792. * Prepares user data for return in an XML-RPC object.
  793. *
  794. * @access protected
  795. *
  796. * @param WP_User $user The unprepared user object
  797. * @param array $fields The subset of user fields to return
  798. * @return array The prepared user data
  799. */
  800. protected function _prepare_user( $user, $fields ) {
  801. $_user = array( 'user_id' => strval( $user->ID ) );
  802. $user_fields = array(
  803. 'username' => $user->user_login,
  804. 'first_name' => $user->user_firstname,
  805. 'last_name' => $user->user_lastname,
  806. 'registered' => $this->_convert_date( $user->user_registered ),
  807. 'bio' => $user->user_description,
  808. 'email' => $user->user_email,
  809. 'nickname' => $user->nickname,
  810. 'nicename' => $user->user_nicename,
  811. 'url' => $user->user_url,
  812. 'display_name' => $user->display_name,
  813. 'roles' => $user->roles,
  814. );
  815. if ( in_array( 'all', $fields ) ) {
  816. $_user = array_merge( $_user, $user_fields );
  817. } else {
  818. if ( in_array( 'basic', $fields ) ) {
  819. $basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' );
  820. $fields = array_merge( $fields, $basic_fields );
  821. }
  822. $requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) );
  823. $_user = array_merge( $_user, $requested_fields );
  824. }
  825. return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields );
  826. }
  827. /**
  828. * Create a new post for any registered post type.
  829. *
  830. * @since 3.4.0
  831. *
  832. * @param array $args Method parameters. Contains:
  833. * - int $blog_id
  834. * - string $username
  835. * - string $password
  836. * - array $content_struct
  837. * $content_struct can contain:
  838. * - post_type (default: 'post')
  839. * - post_status (default: 'draft')
  840. * - post_title
  841. * - post_author
  842. * - post_excerpt
  843. * - post_content
  844. * - post_date_gmt | post_date
  845. * - post_format
  846. * - post_password
  847. * - comment_status - can be 'open' | 'closed'
  848. * - ping_status - can be 'open' | 'closed'
  849. * - sticky
  850. * - post_thumbnail - ID of a media item to use as the post thumbnail/featured image
  851. * - custom_fields - array, with each element containing 'key' and 'value'
  852. * - terms - array, with taxonomy names as keys and arrays of term IDs as values
  853. * - terms_names - array, with taxonomy names as keys and arrays of term names as values
  854. * - enclosure
  855. * - any other fields supported by wp_insert_post()
  856. * @return string post_id
  857. */
  858. function wp_newPost( $args ) {
  859. if ( ! $this->minimum_args( $args, 4 ) )
  860. return $this->error;
  861. $this->escape( $args );
  862. $blog_id = (int) $args[0];
  863. $username = $args[1];
  864. $password = $args[2];
  865. $content_struct = $args[3];
  866. if ( ! $user = $this->login( $username, $password ) )
  867. return $this->error;
  868. do_action( 'xmlrpc_call', 'wp.newPost' );
  869. unset( $content_struct['ID'] );
  870. return $this->_insert_post( $user, $content_struct );
  871. }
  872. /**
  873. * Helper method for filtering out elements from an array.
  874. *
  875. * @since 3.4.0
  876. *
  877. * @param int $count Number to compare to one.
  878. */
  879. private function _is_greater_than_one( $count ) {
  880. return $count > 1;
  881. }
  882. /**
  883. * Helper method for wp_newPost and wp_editPost, containing shared logic.
  884. *
  885. * @since 3.4.0
  886. * @uses wp_insert_post()
  887. *
  888. * @param WP_User $user The post author if post_author isn't set in $content_struct.
  889. * @param array $content_struct Post data to insert.
  890. */
  891. protected function _insert_post( $user, $content_struct ) {
  892. $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0,
  893. 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' );
  894. $post_data = wp_parse_args( $content_struct, $defaults );
  895. $post_type = get_post_type_object( $post_data['post_type'] );
  896. if ( ! $post_type )
  897. return new IXR_Error( 403, __( 'Invalid post type' ) );
  898. $update = ! empty( $post_data['ID'] );
  899. if ( $update ) {
  900. if ( ! get_post( $post_data['ID'] ) )
  901. return new IXR_Error( 401, __( 'Invalid post ID.' ) );
  902. if ( ! current_user_can( $post_type->cap->edit_post, $post_data['ID'] ) )
  903. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
  904. if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) )
  905. return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
  906. } else {
  907. if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) )
  908. return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
  909. }
  910. switch ( $post_data['post_status'] ) {
  911. case 'draft':
  912. case 'pending':
  913. break;
  914. case 'private':
  915. if ( ! current_user_can( $post_type->cap->publish_posts ) )
  916. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type' ) );
  917. break;
  918. case 'publish':
  919. case 'future':
  920. if ( ! current_user_can( $post_type->cap->publish_posts ) )
  921. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) );
  922. break;
  923. default:
  924. if ( ! get_post_status_object( $post_data['post_status'] ) )
  925. $post_data['post_status'] = 'draft';
  926. break;
  927. }
  928. if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) )
  929. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type' ) );
  930. $post_data['post_author'] = absint( $post_data['post_author'] );
  931. if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) {
  932. if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
  933. return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) );
  934. $author = get_userdata( $post_data['post_author'] );
  935. if ( ! $author )
  936. return new IXR_Error( 404, __( 'Invalid author ID.' ) );
  937. } else {
  938. $post_data['post_author'] = $user->ID;
  939. }
  940. if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' )
  941. unset( $post_data['comment_status'] );
  942. if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' )
  943. unset( $post_data['ping_status'] );
  944. // Do some timestamp voodoo
  945. if ( ! empty( $post_data['post_date_gmt'] ) ) {
  946. // We know this is supposed to be GMT, so we're going to slap that Z on there by force
  947. $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z';
  948. } elseif ( ! empty( $post_data['post_date'] ) ) {
  949. $dateCreated = $post_data['post_date']->getIso();
  950. }
  951. if ( ! empty( $dateCreated ) ) {
  952. $post_data['post_date'] = get_date_from_gmt( iso8601_to_datetime( $dateCreated ) );
  953. $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'GMT' );
  954. }
  955. if ( ! isset( $post_data['ID'] ) )
  956. $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID;
  957. $post_ID = $post_data['ID'];
  958. if ( $post_data['post_type'] == 'post' ) {
  959. // Private and password-protected posts cannot be stickied.
  960. if ( $post_data['post_status'] == 'private' || ! empty( $post_data['post_password'] ) ) {
  961. // Error if the client tried to stick the post, otherwise, silently unstick.
  962. if ( ! empty( $post_data['sticky'] ) )
  963. return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
  964. if ( $update )
  965. unstick_post( $post_ID );
  966. } elseif ( isset( $post_data['sticky'] ) ) {
  967. if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
  968. return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
  969. if ( $post_data['sticky'] )
  970. stick_post( $post_ID );
  971. else
  972. unstick_post( $post_ID );
  973. }
  974. }
  975. if ( isset( $post_data['post_thumbnail'] ) ) {
  976. // empty value deletes, non-empty value adds/updates
  977. if ( ! $post_data['post_thumbnail'] )
  978. delete_post_thumbnail( $post_ID );
  979. elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) )
  980. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
  981. set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] );
  982. unset( $content_struct['post_thumbnail'] );
  983. }
  984. if ( isset( $post_data['custom_fields'] ) )
  985. $this->set_custom_fields( $post_ID, $post_data['custom_fields'] );
  986. if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) {
  987. $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' );
  988. // accumulate term IDs from terms and terms_names
  989. $terms = array();
  990. // first validate the terms specified by ID
  991. if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) {
  992. $taxonomies = array_keys( $post_data['terms'] );
  993. // validating term ids
  994. foreach ( $taxonomies as $taxonomy ) {
  995. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
  996. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
  997. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
  998. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
  999. $term_ids = $post_data['terms'][$taxonomy];
  1000. foreach ( $term_ids as $term_id ) {
  1001. $term = get_term_by( 'id', $term_id, $taxonomy );
  1002. if ( ! $term )
  1003. return new IXR_Error( 403, __( 'Invalid term ID' ) );
  1004. $terms[$taxonomy][] = (int) $term_id;
  1005. }
  1006. }
  1007. }
  1008. // now validate terms specified by name
  1009. if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) {
  1010. $taxonomies = array_keys( $post_data['terms_names'] );
  1011. foreach ( $taxonomies as $taxonomy ) {
  1012. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
  1013. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
  1014. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
  1015. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
  1016. // for hierarchical taxonomies, we can't assign a term when multiple terms in the hierarchy share the same name
  1017. $ambiguous_terms = array();
  1018. if ( is_taxonomy_hierarchical( $taxonomy ) ) {
  1019. $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) );
  1020. // count the number of terms with the same name
  1021. $tax_term_names_count = array_count_values( $tax_term_names );
  1022. // filter out non-ambiguous term names
  1023. $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') );
  1024. $ambiguous_terms = array_keys( $ambiguous_tax_term_counts );
  1025. }
  1026. $term_names = $post_data['terms_names'][$taxonomy];
  1027. foreach ( $term_names as $term_name ) {
  1028. if ( in_array( $term_name, $ambiguous_terms ) )
  1029. return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) );
  1030. $term = get_term_by( 'name', $term_name, $taxonomy );
  1031. if ( ! $term ) {
  1032. // term doesn't exist, so check that the user is allowed to create new terms
  1033. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) )
  1034. return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) );
  1035. // create the new term
  1036. $term_info = wp_insert_term( $term_name, $taxonomy );
  1037. if ( is_wp_error( $term_info ) )
  1038. return new IXR_Error( 500, $term_info->get_error_message() );
  1039. $terms[$taxonomy][] = (int) $term_info['term_id'];
  1040. } else {
  1041. $terms[$taxonomy][] = (int) $term->term_id;
  1042. }
  1043. }
  1044. }
  1045. }
  1046. $post_data['tax_input'] = $terms;
  1047. unset( $post_data['terms'], $post_data['terms_names'] );
  1048. } else {
  1049. // do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names'
  1050. unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] );
  1051. }
  1052. if ( isset( $post_data['post_format'] ) ) {
  1053. $format = set_post_format( $post_ID, $post_data['post_format'] );
  1054. if ( is_wp_error( $format ) )
  1055. return new IXR_Error( 500, $format->get_error_message() );
  1056. unset( $post_data['post_format'] );
  1057. }
  1058. // Handle enclosures
  1059. $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null;
  1060. $this->add_enclosure_if_new( $post_ID, $enclosure );
  1061. $this->attach_uploads( $post_ID, $post_data['post_content'] );
  1062. $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
  1063. $post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true );
  1064. if ( is_wp_error( $post_ID ) )
  1065. return new IXR_Error( 500, $post_ID->get_error_message() );
  1066. if ( ! $post_ID )
  1067. return new IXR_Error( 401, __( 'Sorry, your entry could not be posted. Something wrong happened.' ) );
  1068. return strval( $post_ID );
  1069. }
  1070. /**
  1071. * Edit a post for any registered post type.
  1072. *
  1073. * The $content_struct parameter only needs to contain fields that
  1074. * should be changed. All other fields will retain their existing values.
  1075. *
  1076. * @since 3.4.0
  1077. *
  1078. * @param array $args Method parameters. Contains:
  1079. * - int $blog_id
  1080. * - string $username
  1081. * - string $password
  1082. * - int $post_id
  1083. * - array $content_struct
  1084. * @return true on success
  1085. */
  1086. function wp_editPost( $args ) {
  1087. if ( ! $this->minimum_args( $args, 5 ) )
  1088. return $this->error;
  1089. $this->escape( $args );
  1090. $blog_id = (int) $args[0];
  1091. $username = $args[1];
  1092. $password = $args[2];
  1093. $post_id = (int) $args[3];
  1094. $content_struct = $args[4];
  1095. if ( ! $user = $this->login( $username, $password ) )
  1096. return $this->error;
  1097. do_action( 'xmlrpc_call', 'wp.editPost' );
  1098. $post = get_post( $post_id, ARRAY_A );
  1099. if ( empty( $post['ID'] ) )
  1100. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1101. if ( isset( $content_struct['if_not_modified_since'] ) ) {
  1102. // If the post has been modified since the date provided, return an error.
  1103. if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) {
  1104. return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) );
  1105. }
  1106. }
  1107. // convert the date field back to IXR form
  1108. $post['post_date'] = $this->_convert_date( $post['post_date'] );
  1109. // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
  1110. // since _insert_post will ignore the non-GMT date if the GMT date is set
  1111. if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) )
  1112. unset( $post['post_date_gmt'] );
  1113. else
  1114. $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] );
  1115. $this->escape( $post );
  1116. $merged_content_struct = array_merge( $post, $content_struct );
  1117. $retval = $this->_insert_post( $user, $merged_content_struct );
  1118. if ( $retval instanceof IXR_Error )
  1119. return $retval;
  1120. return true;
  1121. }
  1122. /**
  1123. * Delete a post for any registered post type.
  1124. *
  1125. * @since 3.4.0
  1126. *
  1127. * @uses wp_delete_post()
  1128. * @param array $args Method parameters. Contains:
  1129. * - int $blog_id
  1130. * - string $username
  1131. * - string $password
  1132. * - int $post_id
  1133. * @return true on success
  1134. */
  1135. function wp_deletePost( $args ) {
  1136. if ( ! $this->minimum_args( $args, 4 ) )
  1137. return $this->error;
  1138. $this->escape( $args );
  1139. $blog_id = (int) $args[0];
  1140. $username = $args[1];
  1141. $password = $args[2];
  1142. $post_id = (int) $args[3];
  1143. if ( ! $user = $this->login( $username, $password ) )
  1144. return $this->error;
  1145. do_action( 'xmlrpc_call', 'wp.deletePost' );
  1146. $post = get_post( $post_id, ARRAY_A );
  1147. if ( empty( $post['ID'] ) )
  1148. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1149. $post_type = get_post_type_object( $post['post_type'] );
  1150. if ( ! current_user_can( $post_type->cap->delete_post, $post_id ) )
  1151. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) );
  1152. $result = wp_delete_post( $post_id );
  1153. if ( ! $result )
  1154. return new IXR_Error( 500, __( 'The post cannot be deleted.' ) );
  1155. return true;
  1156. }
  1157. /**
  1158. * Retrieve a post.
  1159. *
  1160. * @since 3.4.0
  1161. *
  1162. * The optional $fields parameter specifies what fields will be included
  1163. * in the response array. This should be a list of field names. 'post_id' will
  1164. * always be included in the response regardless of the value of $fields.
  1165. *
  1166. * Instead of, or in addition to, individual field names, conceptual group
  1167. * names can be used to specify multiple fields. The available conceptual
  1168. * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
  1169. * and 'enclosure'.
  1170. *
  1171. * @uses get_post()
  1172. * @param array $args Method parameters. Contains:
  1173. * - int $post_id
  1174. * - string $username
  1175. * - string $password
  1176. * - array $fields optional
  1177. * @return array contains (based on $fields parameter):
  1178. * - 'post_id'
  1179. * - 'post_title'
  1180. * - 'post_date'
  1181. * - 'post_date_gmt'
  1182. * - 'post_modified'
  1183. * - 'post_modified_gmt'
  1184. * - 'post_status'
  1185. * - 'post_type'
  1186. * - 'post_name'
  1187. * - 'post_author'
  1188. * - 'post_password'
  1189. * - 'post_excerpt'
  1190. * - 'post_content'
  1191. * - 'link'
  1192. * - 'comment_status'
  1193. * - 'ping_status'
  1194. * - 'sticky'
  1195. * - 'custom_fields'
  1196. * - 'terms'
  1197. * - 'categories'
  1198. * - 'tags'
  1199. * - 'enclosure'
  1200. */
  1201. function wp_getPost( $args ) {
  1202. if ( ! $this->minimum_args( $args, 4 ) )
  1203. return $this->error;
  1204. $this->escape( $args );
  1205. $blog_id = (int) $args[0];
  1206. $username = $args[1];
  1207. $password = $args[2];
  1208. $post_id = (int) $args[3];
  1209. if ( isset( $args[4] ) )
  1210. $fields = $args[4];
  1211. else
  1212. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' );
  1213. if ( ! $user = $this->login( $username, $password ) )
  1214. return $this->error;
  1215. do_action( 'xmlrpc_call', 'wp.getPost' );
  1216. $post = get_post( $post_id, ARRAY_A );
  1217. if ( empty( $post['ID'] ) )
  1218. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1219. $post_type = get_post_type_object( $post['post_type'] );
  1220. if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) )
  1221. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  1222. return $this->_prepare_post( $post, $fields );
  1223. }
  1224. /**
  1225. * Retrieve posts.
  1226. *
  1227. * @since 3.4.0
  1228. *
  1229. * The optional $filter parameter modifies the query used to retrieve posts.
  1230. * Accepted keys are 'post_type', 'post_status', 'number', 'offset',
  1231. * 'orderby', and 'order'.
  1232. *
  1233. * The optional $fields parameter specifies what fields will be included
  1234. * in the response array.
  1235. *
  1236. * @uses wp_get_recent_posts()
  1237. * @see wp_getPost() for more on $fields
  1238. * @see get_posts() for more on $filter values
  1239. *
  1240. * @param array $args Method parameters. Contains:
  1241. * - int $blog_id
  1242. * - string $username
  1243. * - string $password
  1244. * - array $filter optional
  1245. * - array $fields optional
  1246. * @return array contains a collection of posts.
  1247. */
  1248. function wp_getPosts( $args ) {
  1249. if ( ! $this->minimum_args( $args, 3 ) )
  1250. return $this->error;
  1251. $this->escape( $args );
  1252. $blog_id = (int) $args[0];
  1253. $username = $args[1];
  1254. $password = $args[2];
  1255. $filter = isset( $args[3] ) ? $args[3] : array();
  1256. if ( isset( $args[4] ) )
  1257. $fields = $args[4];
  1258. else
  1259. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' );
  1260. if ( ! $user = $this->login( $username, $password ) )
  1261. return $this->error;
  1262. do_action( 'xmlrpc_call', 'wp.getPosts' );
  1263. $query = array();
  1264. if ( isset( $filter['post_type'] ) ) {
  1265. $post_type = get_post_type_object( $filter['post_type'] );
  1266. if ( ! ( (bool) $post_type ) )
  1267. return new IXR_Error( 403, __( 'The post type specified is not valid' ) );
  1268. } else {
  1269. $post_type = get_post_type_object( 'post' );
  1270. }
  1271. if ( ! current_user_can( $post_type->cap->edit_posts ) )
  1272. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type' ));
  1273. $query['post_type'] = $post_type->name;
  1274. if ( isset( $filter['post_status'] ) )
  1275. $query['post_status'] = $filter['post_status'];
  1276. if ( isset( $filter['number'] ) )
  1277. $query['numberposts'] = absint( $filter['number'] );
  1278. if ( isset( $filter['offset'] ) )
  1279. $query['offset'] = absint( $filter['offset'] );
  1280. if ( isset( $filter['orderby'] ) ) {
  1281. $query['orderby'] = $filter['orderby'];
  1282. if ( isset( $filter['order'] ) )
  1283. $query['order'] = $filter['order'];
  1284. }
  1285. if ( isset( $filter['s'] ) ) {
  1286. $query['s'] = $filter['s'];
  1287. }
  1288. $posts_list = wp_get_recent_posts( $query );
  1289. if ( ! $posts_list )
  1290. return array();
  1291. // holds all the posts data
  1292. $struct = array();
  1293. foreach ( $posts_list as $post ) {
  1294. $post_type = get_post_type_object( $post['post_type'] );
  1295. if ( ! current_user_can( $post_type->cap->edit_post, $post['ID'] ) )
  1296. continue;
  1297. $struct[] = $this->_prepare_post( $post, $fields );
  1298. }
  1299. return $struct;
  1300. }
  1301. /**
  1302. * Create a new term.
  1303. *
  1304. * @since 3.4.0
  1305. *
  1306. * @uses wp_insert_term()
  1307. * @param array $args Method parameters. Contains:
  1308. * - int $blog_id
  1309. * - string $username
  1310. * - string $password
  1311. * - array $content_struct
  1312. * The $content_struct must contain:
  1313. * - 'name'
  1314. * - 'taxonomy'
  1315. * Also, it can optionally contain:
  1316. * - 'parent'
  1317. * - 'description'
  1318. * - 'slug'
  1319. * @return string term_id
  1320. */
  1321. function wp_newTerm( $args ) {
  1322. if ( ! $this->minimum_args( $args, 4 ) )
  1323. return $this->error;
  1324. $this->escape( $args );
  1325. $blog_id = (int) $args[0];
  1326. $username = $args[1];
  1327. $password = $args[2];
  1328. $content_struct = $args[3];
  1329. if ( ! $user = $this->login( $username, $password ) )
  1330. return $this->error;
  1331. do_action( 'xmlrpc_call', 'wp.newTerm' );
  1332. if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
  1333. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1334. $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
  1335. if ( ! current_user_can( $taxonomy->cap->manage_terms ) )
  1336. return new IXR_Error( 401, __( 'You are not allowed to create terms in this taxonomy.' ) );
  1337. $taxonomy = (array) $taxonomy;
  1338. // hold the data of the term
  1339. $term_data = array();
  1340. $term_data['name'] = trim( $content_struct['name'] );
  1341. if ( empty( $term_data['name'] ) )
  1342. return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
  1343. if ( isset( $content_struct['parent'] ) ) {
  1344. if ( ! $taxonomy['hierarchical'] )
  1345. return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) );
  1346. $parent_term_id = (int) $content_struct['parent'];
  1347. $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
  1348. if ( is_wp_error( $parent_term ) )
  1349. return new IXR_Error( 500, $parent_term->get_error_message() );
  1350. if ( ! $parent_term )
  1351. return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
  1352. $term_data['parent'] = $content_struct['parent'];
  1353. }
  1354. if ( isset( $content_struct['description'] ) )
  1355. $term_data['description'] = $content_struct['description'];
  1356. if ( isset( $content_struct['slug'] ) )
  1357. $term_data['slug'] = $content_struct['slug'];
  1358. $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data );
  1359. if ( is_wp_error( $term ) )
  1360. return new IXR_Error( 500, $term->get_error_message() );
  1361. if ( ! $term )
  1362. return new IXR_Error( 500, __( 'Sorry, your term could not be created. Something wrong happened.' ) );
  1363. return strval( $term['term_id'] );
  1364. }
  1365. /**
  1366. * Edit a term.
  1367. *
  1368. * @since 3.4.0
  1369. *
  1370. * @uses wp_update_term()
  1371. * @param array $args Method parameters. Contains:
  1372. * - int $blog_id
  1373. * - string $username
  1374. * - string $password
  1375. * - string $term_id
  1376. * - array $content_struct
  1377. * The $content_struct must contain:
  1378. * - 'taxonomy'
  1379. * Also, it can optionally contain:
  1380. * - 'name'
  1381. * - 'parent'
  1382. * - 'description'
  1383. * - 'slug'
  1384. * @return bool True, on success.
  1385. */
  1386. function wp_editTerm( $args ) {
  1387. if ( ! $this->minimum_args( $args, 5 ) )
  1388. return $this->error;
  1389. $this->escape( $args );
  1390. $blog_id = (int) $args[0];
  1391. $username = $args[1];
  1392. $password = $args[2];
  1393. $term_id = (int) $args[3];
  1394. $content_struct = $args[4];
  1395. if ( ! $user = $this->login( $username, $password ) )
  1396. return $this->error;
  1397. do_action( 'xmlrpc_call', 'wp.editTerm' );
  1398. if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
  1399. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1400. $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
  1401. if ( ! current_user_can( $taxonomy->cap->edit_terms ) )
  1402. return new IXR_Error( 401, __( 'You are not allowed to edit terms in this taxonomy.' ) );
  1403. $taxonomy = (array) $taxonomy;
  1404. // hold the data of the term
  1405. $term_data = array();
  1406. $term = get_term( $term_id , $content_struct['taxonomy'] );
  1407. if ( is_wp_error( $term ) )
  1408. return new IXR_Error( 500, $term->get_error_message() );
  1409. if ( ! $term )
  1410. return new IXR_Error( 404, __( 'Invalid term ID' ) );
  1411. if ( isset( $content_struct['name'] ) ) {
  1412. $term_data['name'] = trim( $content_struct['name'] );
  1413. if ( empty( $term_data['name'] ) )
  1414. return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
  1415. }
  1416. if ( isset( $content_struct['parent'] ) ) {
  1417. if ( ! $taxonomy['hierarchical'] )
  1418. return new IXR_Error( 403, __( "This taxonomy is not hierarchical so you can't set a parent." ) );
  1419. $parent_term_id = (int) $content_struct['parent'];
  1420. $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
  1421. if ( is_wp_error( $parent_term ) )
  1422. return new IXR_Error( 500, $parent_term->get_error_message() );
  1423. if ( ! $parent_term )
  1424. return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
  1425. $term_data['parent'] = $content_struct['parent'];
  1426. }
  1427. if ( isset( $content_struct['description'] ) )
  1428. $term_data['description'] = $content_struct['description'];
  1429. if ( isset( $content_struct['slug'] ) )
  1430. $term_data['slug'] = $content_struct['slug'];
  1431. $term = wp_update_term( $term_id , $taxonomy['name'] , $term_data );
  1432. if ( is_wp_error( $term ) )
  1433. return new IXR_Error( 500, $term->get_error_message() );
  1434. if ( ! $term )
  1435. return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) );
  1436. return true;
  1437. }
  1438. /**
  1439. * Delete a term.
  1440. *
  1441. * @since 3.4.0
  1442. *
  1443. * @uses wp_delete_term()
  1444. * @param array $args Method parameters. Contains:
  1445. * - int $blog_id
  1446. * - string $username
  1447. * - string $password
  1448. * - string $taxnomy_name
  1449. * - string $term_id
  1450. * @return boolean|IXR_Error If it suceeded true else a reason why not
  1451. */
  1452. function wp_deleteTerm( $args ) {
  1453. if ( ! $this->minimum_args( $args, 5 ) )
  1454. return $this->error;
  1455. $this->escape( $args );
  1456. $blog_id = (int) $args[0];
  1457. $username = $args[1];
  1458. $password = $args[2];
  1459. $taxonomy = $args[3];
  1460. $term_id = (int) $args[4];
  1461. if ( ! $user = $this->login( $username, $password ) )
  1462. return $this->error;
  1463. do_action( 'xmlrpc_call', 'wp.deleteTerm' );
  1464. if ( ! taxonomy_exists( $taxonomy ) )
  1465. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1466. $taxonomy = get_taxonomy( $taxonomy );
  1467. if ( ! current_user_can( $taxonomy->cap->delete_terms ) )
  1468. return new IXR_Error( 401, __( 'You are not allowed to delete terms in this taxonomy.' ) );
  1469. $term = get_term( $term_id, $taxonomy->name );
  1470. if ( is_wp_error( $term ) )
  1471. return new IXR_Error( 500, $term->get_error_message() );
  1472. if ( ! $term )
  1473. return new IXR_Error( 404, __( 'Invalid term ID' ) );
  1474. $result = wp_delete_term( $term_id, $taxonomy->name );
  1475. if ( is_wp_error( $result ) )
  1476. return new IXR_Error( 500, $term->get_error_message() );
  1477. if ( ! $result )
  1478. return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) );
  1479. return $result;
  1480. }
  1481. /**
  1482. * Retrieve a term.
  1483. *
  1484. * @since 3.4.0
  1485. *
  1486. * @uses get_term()
  1487. * @param array $args Method parameters. Contains:
  1488. * - int $blog_id
  1489. * - string $username
  1490. * - string $password
  1491. * - string $taxonomy
  1492. * - string $term_id
  1493. * @return array contains:
  1494. * - 'term_id'
  1495. * - 'name'
  1496. * - 'slug'
  1497. * - 'term_group'
  1498. * - 'term_taxonomy_id'
  1499. * - 'taxonomy'
  1500. * - 'description'
  1501. * - 'parent'
  1502. * - 'count'
  1503. */
  1504. function wp_getTerm( $args ) {
  1505. if ( ! $this->minimum_args( $args, 5 ) )
  1506. return $this->error;
  1507. $this->escape( $args );
  1508. $blog_id = (int) $args[0];
  1509. $username = $args[1];
  1510. $password = $args[2];
  1511. $taxonomy = $args[3];
  1512. $term_id = (int) $args[4];
  1513. if ( ! $user = $this->login( $username, $password ) )
  1514. return $this->error;
  1515. do_action( 'xmlrpc_call', 'wp.getTerm' );
  1516. if ( ! taxonomy_exists( $taxonomy ) )
  1517. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1518. $taxonomy = get_taxonomy( $taxonomy );
  1519. if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
  1520. return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
  1521. $term = get_term( $term_id , $taxonomy->name, ARRAY_A );
  1522. if ( is_wp_error( $term ) )
  1523. return new IXR_Error( 500, $term->get_error_message() );
  1524. if ( ! $term )
  1525. return new IXR_Error( 404, __( 'Invalid term ID' ) );
  1526. return $this->_prepare_term( $term );
  1527. }
  1528. /**
  1529. * Retrieve all terms for a taxonomy.
  1530. *
  1531. * @since 3.4.0
  1532. *
  1533. * The optional $filter parameter modifies the query used to retrieve terms.
  1534. * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'.
  1535. *
  1536. * @uses get_terms()
  1537. * @param array $args Method parameters. Contains:
  1538. * - int $blog_id
  1539. * - string $username
  1540. * - string $password
  1541. * - string $taxonomy
  1542. * - array $filter optional
  1543. * @return array terms
  1544. */
  1545. function wp_getTerms( $args ) {
  1546. if ( ! $this->minimum_args( $args, 4 ) )
  1547. return $this->error;
  1548. $this->escape( $args );
  1549. $blog_id = (int) $args[0];
  1550. $username = $args[1];
  1551. $password = $args[2];
  1552. $taxonomy = $args[3];
  1553. $filter = isset( $args[4] ) ? $args[4] : array();
  1554. if ( ! $user = $this->login( $username, $password ) )
  1555. return $this->error;
  1556. do_action( 'xmlrpc_call', 'wp.getTerms' );
  1557. if ( ! taxonomy_exists( $taxonomy ) )
  1558. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1559. $taxonomy = get_taxonomy( $taxonomy );
  1560. if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
  1561. return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
  1562. $query = array();
  1563. if ( isset( $filter['number'] ) )
  1564. $query['number'] = absint( $filter['number'] );
  1565. if ( isset( $filter['offset'] ) )
  1566. $query['offset'] = absint( $filter['offset'] );
  1567. if ( isset( $filter['orderby'] ) ) {
  1568. $query['orderby'] = $filter['orderby'];
  1569. if ( isset( $filter['order'] ) )
  1570. $query['order'] = $filter['order'];
  1571. }
  1572. if ( isset( $filter['hide_empty'] ) )
  1573. $query['hide_empty'] = $filter['hide_empty'];
  1574. else
  1575. $query['get'] = 'all';
  1576. if ( isset( $filter['search'] ) )
  1577. $query['search'] = $filter['search'];
  1578. $terms = get_terms( $taxonomy->name, $query );
  1579. if ( is_wp_error( $terms ) )
  1580. return new IXR_Error( 500, $terms->get_error_message() );
  1581. $struct = array();
  1582. foreach ( $terms as $term ) {
  1583. $struct[] = $this->_prepare_term( $term );
  1584. }
  1585. return $struct;
  1586. }
  1587. /**
  1588. * Retrieve a taxonomy.
  1589. *
  1590. * @since 3.4.0
  1591. *
  1592. * @uses get_taxonomy()
  1593. * @param array $args Method parameters. Contains:
  1594. * - int $blog_id
  1595. * - string $username
  1596. * - string $password
  1597. * - string $taxonomy
  1598. * @return array (@see get_taxonomy())
  1599. */
  1600. function wp_getTaxonomy( $args ) {
  1601. if ( ! $this->minimum_args( $args, 4 ) )
  1602. return $this->error;
  1603. $this->escape( $args );
  1604. $blog_id = (int) $args[0];
  1605. $username = $args[1];
  1606. $password = $args[2];
  1607. $taxonomy = $args[3];
  1608. if ( isset( $args[4] ) )
  1609. $fields = $args[4];
  1610. else
  1611. $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' );
  1612. if ( ! $user = $this->login( $username, $password ) )
  1613. return $this->error;
  1614. do_action( 'xmlrpc_call', 'wp.getTaxonomy' );
  1615. if ( ! taxonomy_exists( $taxonomy ) )
  1616. return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
  1617. $taxonomy = get_taxonomy( $taxonomy );
  1618. if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
  1619. return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
  1620. return $this->_prepare_taxonomy( $taxonomy, $fields );
  1621. }
  1622. /**
  1623. * Retrieve all taxonomies.
  1624. *
  1625. * @since 3.4.0
  1626. *
  1627. * @uses get_taxonomies()
  1628. * @param array $args Method parameters. Contains:
  1629. * - int $blog_id
  1630. * - string $username
  1631. * - string $password
  1632. * @return array taxonomies
  1633. */
  1634. function wp_getTaxonomies( $args ) {
  1635. if ( ! $this->minimum_args( $args, 3 ) )
  1636. return $this->error;
  1637. $this->escape( $args );
  1638. $blog_id = (int) $args[0];
  1639. $username = $args[1];
  1640. $password = $args[2];
  1641. $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true );
  1642. if ( isset( $args[4] ) )
  1643. $fields = $args[4];
  1644. else
  1645. $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' );
  1646. if ( ! $user = $this->login( $username, $password ) )
  1647. return $this->error;
  1648. do_action( 'xmlrpc_call', 'wp.getTaxonomies' );
  1649. $taxonomies = get_taxonomies( $filter, 'objects' );
  1650. // holds all the taxonomy data
  1651. $struct = array();
  1652. foreach ( $taxonomies as $taxonomy ) {
  1653. // capability check for post_types
  1654. if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
  1655. continue;
  1656. $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields );
  1657. }
  1658. return $struct;
  1659. }
  1660. /**
  1661. * Retrieve a user.
  1662. *
  1663. * The optional $fields parameter specifies what fields will be included
  1664. * in the response array. This should be a list of field names. 'user_id' will
  1665. * always be included in the response regardless of the value of $fields.
  1666. *
  1667. * Instead of, or in addition to, individual field names, conceptual group
  1668. * names can be used to specify multiple fields. The available conceptual
  1669. * groups are 'basic' and 'all'.
  1670. *
  1671. * @uses get_userdata()
  1672. * @param array $args Method parameters. Contains:
  1673. * - int $blog_id
  1674. * - string $username
  1675. * - string $password
  1676. * - int $user_id
  1677. * - array $fields optional
  1678. * @return array contains (based on $fields parameter):
  1679. * - 'user_id'
  1680. * - 'username'
  1681. * - 'first_name'
  1682. * - 'last_name'
  1683. * - 'registered'
  1684. * - 'bio'
  1685. * - 'email'
  1686. * - 'nickname'
  1687. * - 'nicename'
  1688. * - 'url'
  1689. * - 'display_name'
  1690. * - 'roles'
  1691. */
  1692. function wp_getUser( $args ) {
  1693. if ( ! $this->minimum_args( $args, 4 ) )
  1694. return $this->error;
  1695. $this->escape( $args );
  1696. $blog_id = (int) $args[0];
  1697. $username = $args[1];
  1698. $password = $args[2];
  1699. $user_id = (int) $args[3];
  1700. if ( isset( $args[4] ) )
  1701. $fields = $args[4];
  1702. else
  1703. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' );
  1704. if ( ! $user = $this->login( $username, $password ) )
  1705. return $this->error;
  1706. do_action( 'xmlrpc_call', 'wp.getUser' );
  1707. if ( ! current_user_can( 'edit_user', $user_id ) )
  1708. return new IXR_Error( 401, __( 'Sorry, you cannot edit users.' ) );
  1709. $user_data = get_userdata( $user_id );
  1710. if ( ! $user_data )
  1711. return new IXR_Error( 404, __( 'Invalid user ID' ) );
  1712. return $this->_prepare_user( $user_data, $fields );
  1713. }
  1714. /**
  1715. * Retrieve users.
  1716. *
  1717. * The optional $filter parameter modifies the query used to retrieve users.
  1718. * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role',
  1719. * 'who', 'orderby', and 'order'.
  1720. *
  1721. * The optional $fields parameter specifies what fields will be included
  1722. * in the response array.
  1723. *
  1724. * @uses get_users()
  1725. * @see wp_getUser() for more on $fields and return values
  1726. *
  1727. * @param array $args Method parameters. Contains:
  1728. * - int $blog_id
  1729. * - string $username
  1730. * - string $password
  1731. * - array $filter optional
  1732. * - array $fields optional
  1733. * @return array users data
  1734. */
  1735. function wp_getUsers( $args ) {
  1736. if ( ! $this->minimum_args( $args, 3 ) )
  1737. return $this->error;
  1738. $this->escape( $args );
  1739. $blog_id = (int) $args[0];
  1740. $username = $args[1];
  1741. $password = $args[2];
  1742. $filter = isset( $args[3] ) ? $args[3] : array();
  1743. if ( isset( $args[4] ) )
  1744. $fields = $args[4];
  1745. else
  1746. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' );
  1747. if ( ! $user = $this->login( $username, $password ) )
  1748. return $this->error;
  1749. do_action( 'xmlrpc_call', 'wp.getUsers' );
  1750. if ( ! current_user_can( 'list_users' ) )
  1751. return new IXR_Error( 401, __( 'Sorry, you cannot list users.' ) );
  1752. $query = array( 'fields' => 'all_with_meta' );
  1753. $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50;
  1754. $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0;
  1755. if ( isset( $filter['orderby'] ) ) {
  1756. $query['orderby'] = $filter['orderby'];
  1757. if ( isset( $filter['order'] ) )
  1758. $query['order'] = $filter['order'];
  1759. }
  1760. if ( isset( $filter['role'] ) ) {
  1761. if ( get_role( $filter['role'] ) === null )
  1762. return new IXR_Error( 403, __( 'The role specified is not valid' ) );
  1763. $query['role'] = $filter['role'];
  1764. }
  1765. if ( isset( $filter['who'] ) ) {
  1766. $query['who'] = $filter['who'];
  1767. }
  1768. $users = get_users( $query );
  1769. $_users = array();
  1770. foreach ( $users as $user_data ) {
  1771. if ( current_user_can( 'edit_user', $user_data->ID ) )
  1772. $_users[] = $this->_prepare_user( $user_data, $fields );
  1773. }
  1774. return $_users;
  1775. }
  1776. /**
  1777. * Retrieve information about the requesting user.
  1778. *
  1779. * @uses get_userdata()
  1780. * @param array $args Method parameters. Contains:
  1781. * - int $blog_id
  1782. * - string $username
  1783. * - string $password
  1784. * - array $fields optional
  1785. * @return array (@see wp_getUser)
  1786. */
  1787. function wp_getProfile( $args ) {
  1788. if ( ! $this->minimum_args( $args, 3 ) )
  1789. return $this->error;
  1790. $this->escape( $args );
  1791. $blog_id = (int) $args[0];
  1792. $username = $args[1];
  1793. $password = $args[2];
  1794. if ( isset( $args[3] ) )
  1795. $fields = $args[3];
  1796. else
  1797. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' );
  1798. if ( ! $user = $this->login( $username, $password ) )
  1799. return $this->error;
  1800. do_action( 'xmlrpc_call', 'wp.getProfile' );
  1801. if ( ! current_user_can( 'edit_user', $user->ID ) )
  1802. return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) );
  1803. $user_data = get_userdata( $user->ID );
  1804. return $this->_prepare_user( $user_data, $fields );
  1805. }
  1806. /**
  1807. * Edit user's profile.
  1808. *
  1809. * @uses wp_update_user()
  1810. * @param array $args Method parameters. Contains:
  1811. * - int $blog_id
  1812. * - string $username
  1813. * - string $password
  1814. * - array $content_struct
  1815. * It can optionally contain:
  1816. * - 'first_name'
  1817. * - 'last_name'
  1818. * - 'website'
  1819. * - 'display_name'
  1820. * - 'nickname'
  1821. * - 'nicename'
  1822. * - 'bio'
  1823. * @return bool True, on success.
  1824. */
  1825. function wp_editProfile( $args ) {
  1826. if ( ! $this->minimum_args( $args, 4 ) )
  1827. return $this->error;
  1828. $this->escape( $args );
  1829. $blog_id = (int) $args[0];
  1830. $username = $args[1];
  1831. $password = $args[2];
  1832. $content_struct = $args[3];
  1833. if ( ! $user = $this->login( $username, $password ) )
  1834. return $this->error;
  1835. do_action( 'xmlrpc_call', 'wp.editProfile' );
  1836. if ( ! current_user_can( 'edit_user', $user->ID ) )
  1837. return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) );
  1838. // holds data of the user
  1839. $user_data = array();
  1840. $user_data['ID'] = $user->ID;
  1841. // only set the user details if it was given
  1842. if ( isset( $content_struct['first_name'] ) )
  1843. $user_data['first_name'] = $content_struct['first_name'];
  1844. if ( isset( $content_struct['last_name'] ) )
  1845. $user_data['last_name'] = $content_struct['last_name'];
  1846. if ( isset( $content_struct['url'] ) )
  1847. $user_data['user_url'] = $content_struct['url'];
  1848. if ( isset( $content_struct['display_name'] ) )
  1849. $user_data['display_name'] = $content_struct['display_name'];
  1850. if ( isset( $content_struct['nickname'] ) )
  1851. $user_data['nickname'] = $content_struct['nickname'];
  1852. if ( isset( $content_struct['nicename'] ) )
  1853. $user_data['user_nicename'] = $content_struct['nicename'];
  1854. if ( isset( $content_struct['bio'] ) )
  1855. $user_data['description'] = $content_struct['bio'];
  1856. $result = wp_update_user( $user_data );
  1857. if ( is_wp_error( $result ) )
  1858. return new IXR_Error( 500, $result->get_error_message() );
  1859. if ( ! $result )
  1860. return new IXR_Error( 500, __( 'Sorry, the user cannot be updated.' ) );
  1861. return true;
  1862. }
  1863. /**
  1864. * Retrieve page.
  1865. *
  1866. * @since 2.2.0
  1867. *
  1868. * @param array $args Method parameters. Contains:
  1869. * - blog_id
  1870. * - page_id
  1871. * - username
  1872. * - password
  1873. * @return array
  1874. */
  1875. function wp_getPage($args) {
  1876. $this->escape($args);
  1877. $blog_id = (int) $args[0];
  1878. $page_id = (int) $args[1];
  1879. $username = $args[2];
  1880. $password = $args[3];
  1881. if ( !$user = $this->login($username, $password) ) {
  1882. return $this->error;
  1883. }
  1884. $page = get_post($page_id);
  1885. if ( ! $page )
  1886. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  1887. if ( !current_user_can( 'edit_page', $page_id ) )
  1888. return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );
  1889. do_action('xmlrpc_call', 'wp.getPage');
  1890. // If we found the page then format the data.
  1891. if ( $page->ID && ($page->post_type == 'page') ) {
  1892. return $this->_prepare_page( $page );
  1893. }
  1894. // If the page doesn't exist indicate that.
  1895. else {
  1896. return(new IXR_Error(404, __('Sorry, no such page.')));
  1897. }
  1898. }
  1899. /**
  1900. * Retrieve Pages.
  1901. *
  1902. * @since 2.2.0
  1903. *
  1904. * @param array $args Method parameters. Contains:
  1905. * - blog_id
  1906. * - username
  1907. * - password
  1908. * - num_pages
  1909. * @return array
  1910. */
  1911. function wp_getPages($args) {
  1912. $this->escape($args);
  1913. $blog_id = (int) $args[0];
  1914. $username = $args[1];
  1915. $password = $args[2];
  1916. $num_pages = isset($args[3]) ? (int) $args[3] : 10;
  1917. if ( !$user = $this->login($username, $password) )
  1918. return $this->error;
  1919. if ( !current_user_can( 'edit_pages' ) )
  1920. return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
  1921. do_action('xmlrpc_call', 'wp.getPages');
  1922. $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );
  1923. $num_pages = count($pages);
  1924. // If we have pages, put together their info.
  1925. if ( $num_pages >= 1 ) {
  1926. $pages_struct = array();
  1927. foreach ($pages as $page) {
  1928. if ( current_user_can( 'edit_page', $page->ID ) )
  1929. $pages_struct[] = $this->_prepare_page( $page );
  1930. }
  1931. return($pages_struct);
  1932. }
  1933. // If no pages were found return an error.
  1934. else {
  1935. return(array());
  1936. }
  1937. }
  1938. /**
  1939. * Create new page.
  1940. *
  1941. * @since 2.2.0
  1942. *
  1943. * @param array $args Method parameters. See {@link wp_xmlrpc_server::mw_newPost()}
  1944. * @return unknown
  1945. */
  1946. function wp_newPage($args) {
  1947. // Items not escaped here will be escaped in newPost.
  1948. $username = $this->escape($args[1]);
  1949. $password = $this->escape($args[2]);
  1950. $page = $args[3];
  1951. $publish = $args[4];
  1952. if ( !$user = $this->login($username, $password) )
  1953. return $this->error;
  1954. do_action('xmlrpc_call', 'wp.newPage');
  1955. // Mark this as content for a page.
  1956. $args[3]["post_type"] = 'page';
  1957. // Let mw_newPost do all of the heavy lifting.
  1958. return($this->mw_newPost($args));
  1959. }
  1960. /**
  1961. * Delete page.
  1962. *
  1963. * @since 2.2.0
  1964. *
  1965. * @param array $args Method parameters.
  1966. * @return bool True, if success.
  1967. */
  1968. function wp_deletePage($args) {
  1969. $this->escape($args);
  1970. $blog_id = (int) $args[0];
  1971. $username = $args[1];
  1972. $password = $args[2];
  1973. $page_id = (int) $args[3];
  1974. if ( !$user = $this->login($username, $password) )
  1975. return $this->error;
  1976. do_action('xmlrpc_call', 'wp.deletePage');
  1977. // Get the current page based on the page_id and
  1978. // make sure it is a page and not a post.
  1979. $actual_page = get_post($page_id, ARRAY_A);
  1980. if ( !$actual_page || ($actual_page['post_type'] != 'page') )
  1981. return(new IXR_Error(404, __('Sorry, no such page.')));
  1982. // Make sure the user can delete pages.
  1983. if ( !current_user_can('delete_page', $page_id) )
  1984. return(new IXR_Error(401, __('Sorry, you do not have the right to delete this page.')));
  1985. // Attempt to delete the page.
  1986. $result = wp_delete_post($page_id);
  1987. if ( !$result )
  1988. return(new IXR_Error(500, __('Failed to delete the page.')));
  1989. do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args );
  1990. return(true);
  1991. }
  1992. /**
  1993. * Edit page.
  1994. *
  1995. * @since 2.2.0
  1996. *
  1997. * @param array $args Method parameters.
  1998. * @return unknown
  1999. */
  2000. function wp_editPage($args) {
  2001. // Items not escaped here will be escaped in editPost.
  2002. $blog_id = (int) $args[0];
  2003. $page_id = (int) $this->escape($args[1]);
  2004. $username = $this->escape($args[2]);
  2005. $password = $this->escape($args[3]);
  2006. $content = $args[4];
  2007. $publish = $args[5];
  2008. if ( !$user = $this->login($username, $password) )
  2009. return $this->error;
  2010. do_action('xmlrpc_call', 'wp.editPage');
  2011. // Get the page data and make sure it is a page.
  2012. $actual_page = get_post($page_id, ARRAY_A);
  2013. if ( !$actual_page || ($actual_page['post_type'] != 'page') )
  2014. return(new IXR_Error(404, __('Sorry, no such page.')));
  2015. // Make sure the user is allowed to edit pages.
  2016. if ( !current_user_can('edit_page', $page_id) )
  2017. return(new IXR_Error(401, __('Sorry, you do not have the right to edit this page.')));
  2018. // Mark this as content for a page.
  2019. $content['post_type'] = 'page';
  2020. // Arrange args in the way mw_editPost understands.
  2021. $args = array(
  2022. $page_id,
  2023. $username,
  2024. $password,
  2025. $content,
  2026. $publish
  2027. );
  2028. // Let mw_editPost do all of the heavy lifting.
  2029. return($this->mw_editPost($args));
  2030. }
  2031. /**
  2032. * Retrieve page list.
  2033. *
  2034. * @since 2.2.0
  2035. *
  2036. * @param array $args Method parameters.
  2037. * @return unknown
  2038. */
  2039. function wp_getPageList($args) {
  2040. global $wpdb;
  2041. $this->escape($args);
  2042. $blog_id = (int) $args[0];
  2043. $username = $args[1];
  2044. $password = $args[2];
  2045. if ( !$user = $this->login($username, $password) )
  2046. return $this->error;
  2047. if ( !current_user_can( 'edit_pages' ) )
  2048. return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
  2049. do_action('xmlrpc_call', 'wp.getPageList');
  2050. // Get list of pages ids and titles
  2051. $page_list = $wpdb->get_results("
  2052. SELECT ID page_id,
  2053. post_title page_title,
  2054. post_parent page_parent_id,
  2055. post_date_gmt,
  2056. post_date,
  2057. post_status
  2058. FROM {$wpdb->posts}
  2059. WHERE post_type = 'page'
  2060. ORDER BY ID
  2061. ");
  2062. // The date needs to be formatted properly.
  2063. $num_pages = count($page_list);
  2064. for ( $i = 0; $i < $num_pages; $i++ ) {
  2065. $page_list[$i]->dateCreated = $this->_convert_date( $page_list[$i]->post_date );
  2066. $page_list[$i]->date_created_gmt = $this->_convert_date_gmt( $page_list[$i]->post_date_gmt, $page_list[$i]->post_date );
  2067. unset($page_list[$i]->post_date_gmt);
  2068. unset($page_list[$i]->post_date);
  2069. unset($page_list[$i]->post_status);
  2070. }
  2071. return($page_list);
  2072. }
  2073. /**
  2074. * Retrieve authors list.
  2075. *
  2076. * @since 2.2.0
  2077. *
  2078. * @param array $args Method parameters.
  2079. * @return array
  2080. */
  2081. function wp_getAuthors($args) {
  2082. $this->escape($args);
  2083. $blog_id = (int) $args[0];
  2084. $username = $args[1];
  2085. $password = $args[2];
  2086. if ( !$user = $this->login($username, $password) )
  2087. return $this->error;
  2088. if ( !current_user_can('edit_posts') )
  2089. return(new IXR_Error(401, __('Sorry, you cannot edit posts on this site.')));
  2090. do_action('xmlrpc_call', 'wp.getAuthors');
  2091. $authors = array();
  2092. foreach ( get_users( array( 'fields' => array('ID','user_login','display_name') ) ) as $user ) {
  2093. $authors[] = array(
  2094. 'user_id' => $user->ID,
  2095. 'user_login' => $user->user_login,
  2096. 'display_name' => $user->display_name
  2097. );
  2098. }
  2099. return $authors;
  2100. }
  2101. /**
  2102. * Get list of all tags
  2103. *
  2104. * @since 2.7.0
  2105. *
  2106. * @param array $args Method parameters.
  2107. * @return array
  2108. */
  2109. function wp_getTags( $args ) {
  2110. $this->escape( $args );
  2111. $blog_id = (int) $args[0];
  2112. $username = $args[1];
  2113. $password = $args[2];
  2114. if ( !$user = $this->login($username, $password) )
  2115. return $this->error;
  2116. if ( !current_user_can( 'edit_posts' ) )
  2117. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
  2118. do_action( 'xmlrpc_call', 'wp.getKeywords' );
  2119. $tags = array();
  2120. if ( $all_tags = get_tags() ) {
  2121. foreach( (array) $all_tags as $tag ) {
  2122. $struct['tag_id'] = $tag->term_id;
  2123. $struct['name'] = $tag->name;
  2124. $struct['count'] = $tag->count;
  2125. $struct['slug'] = $tag->slug;
  2126. $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) );
  2127. $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) );
  2128. $tags[] = $struct;
  2129. }
  2130. }
  2131. return $tags;
  2132. }
  2133. /**
  2134. * Create new category.
  2135. *
  2136. * @since 2.2.0
  2137. *
  2138. * @param array $args Method parameters.
  2139. * @return int Category ID.
  2140. */
  2141. function wp_newCategory($args) {
  2142. $this->escape($args);
  2143. $blog_id = (int) $args[0];
  2144. $username = $args[1];
  2145. $password = $args[2];
  2146. $category = $args[3];
  2147. if ( !$user = $this->login($username, $password) )
  2148. return $this->error;
  2149. do_action('xmlrpc_call', 'wp.newCategory');
  2150. // Make sure the user is allowed to add a category.
  2151. if ( !current_user_can('manage_categories') )
  2152. return(new IXR_Error(401, __('Sorry, you do not have the right to add a category.')));
  2153. // If no slug was provided make it empty so that
  2154. // WordPress will generate one.
  2155. if ( empty($category['slug']) )
  2156. $category['slug'] = '';
  2157. // If no parent_id was provided make it empty
  2158. // so that it will be a top level page (no parent).
  2159. if ( !isset($category['parent_id']) )
  2160. $category['parent_id'] = '';
  2161. // If no description was provided make it empty.
  2162. if ( empty($category["description"]) )
  2163. $category["description"] = "";
  2164. $new_category = array(
  2165. 'cat_name' => $category['name'],
  2166. 'category_nicename' => $category['slug'],
  2167. 'category_parent' => $category['parent_id'],
  2168. 'category_description' => $category['description']
  2169. );
  2170. $cat_id = wp_insert_category($new_category, true);
  2171. if ( is_wp_error( $cat_id ) ) {
  2172. if ( 'term_exists' == $cat_id->get_error_code() )
  2173. return (int) $cat_id->get_error_data();
  2174. else
  2175. return(new IXR_Error(500, __('Sorry, the new category failed.')));
  2176. } elseif ( ! $cat_id ) {
  2177. return(new IXR_Error(500, __('Sorry, the new category failed.')));
  2178. }
  2179. do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args );
  2180. return $cat_id;
  2181. }
  2182. /**
  2183. * Remove category.
  2184. *
  2185. * @since 2.5.0
  2186. *
  2187. * @param array $args Method parameters.
  2188. * @return mixed See {@link wp_delete_term()} for return info.
  2189. */
  2190. function wp_deleteCategory($args) {
  2191. $this->escape($args);
  2192. $blog_id = (int) $args[0];
  2193. $username = $args[1];
  2194. $password = $args[2];
  2195. $category_id = (int) $args[3];
  2196. if ( !$user = $this->login($username, $password) )
  2197. return $this->error;
  2198. do_action('xmlrpc_call', 'wp.deleteCategory');
  2199. if ( !current_user_can('manage_categories') )
  2200. return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete a category.' ) );
  2201. $status = wp_delete_term( $category_id, 'category' );
  2202. if( true == $status )
  2203. do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args );
  2204. return $status;
  2205. }
  2206. /**
  2207. * Retrieve category list.
  2208. *
  2209. * @since 2.2.0
  2210. *
  2211. * @param array $args Method parameters.
  2212. * @return array
  2213. */
  2214. function wp_suggestCategories($args) {
  2215. $this->escape($args);
  2216. $blog_id = (int) $args[0];
  2217. $username = $args[1];
  2218. $password = $args[2];
  2219. $category = $args[3];
  2220. $max_results = (int) $args[4];
  2221. if ( !$user = $this->login($username, $password) )
  2222. return $this->error;
  2223. if ( !current_user_can( 'edit_posts' ) )
  2224. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) );
  2225. do_action('xmlrpc_call', 'wp.suggestCategories');
  2226. $category_suggestions = array();
  2227. $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);
  2228. foreach ( (array) get_categories($args) as $cat ) {
  2229. $category_suggestions[] = array(
  2230. 'category_id' => $cat->term_id,
  2231. 'category_name' => $cat->name
  2232. );
  2233. }
  2234. return($category_suggestions);
  2235. }
  2236. /**
  2237. * Retrieve comment.
  2238. *
  2239. * @since 2.7.0
  2240. *
  2241. * @param array $args Method parameters.
  2242. * @return array
  2243. */
  2244. function wp_getComment($args) {
  2245. $this->escape($args);
  2246. $blog_id = (int) $args[0];
  2247. $username = $args[1];
  2248. $password = $args[2];
  2249. $comment_id = (int) $args[3];
  2250. if ( !$user = $this->login($username, $password) )
  2251. return $this->error;
  2252. if ( !current_user_can( 'moderate_comments' ) )
  2253. return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
  2254. do_action('xmlrpc_call', 'wp.getComment');
  2255. if ( ! $comment = get_comment($comment_id) )
  2256. return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
  2257. return $this->_prepare_comment( $comment );
  2258. }
  2259. /**
  2260. * Retrieve comments.
  2261. *
  2262. * Besides the common blog_id, username, and password arguments, it takes a filter
  2263. * array as last argument.
  2264. *
  2265. * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'.
  2266. *
  2267. * The defaults are as follows:
  2268. * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold')
  2269. * - 'post_id' - Default is ''. The post where the comment is posted. Empty string shows all comments.
  2270. * - 'number' - Default is 10. Total number of media items to retrieve.
  2271. * - 'offset' - Default is 0. See {@link WP_Query::query()} for more.
  2272. *
  2273. * @since 2.7.0
  2274. *
  2275. * @param array $args Method parameters.
  2276. * @return array. Contains a collection of comments. See {@link wp_xmlrpc_server::wp_getComment()} for a description of each item contents
  2277. */
  2278. function wp_getComments($args) {
  2279. $this->escape($args);
  2280. $blog_id = (int) $args[0];
  2281. $username = $args[1];
  2282. $password = $args[2];
  2283. $struct = isset( $args[3] ) ? $args[3] : array();
  2284. if ( !$user = $this->login($username, $password) )
  2285. return $this->error;
  2286. if ( !current_user_can( 'moderate_comments' ) )
  2287. return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) );
  2288. do_action('xmlrpc_call', 'wp.getComments');
  2289. if ( isset($struct['status']) )
  2290. $status = $struct['status'];
  2291. else
  2292. $status = '';
  2293. $post_id = '';
  2294. if ( isset($struct['post_id']) )
  2295. $post_id = absint($struct['post_id']);
  2296. $offset = 0;
  2297. if ( isset($struct['offset']) )
  2298. $offset = absint($struct['offset']);
  2299. $number = 10;
  2300. if ( isset($struct['number']) )
  2301. $number = absint($struct['number']);
  2302. $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );
  2303. $comments_struct = array();
  2304. foreach ( $comments as $comment ) {
  2305. $comments_struct[] = $this->_prepare_comment( $comment );
  2306. }
  2307. return $comments_struct;
  2308. }
  2309. /**
  2310. * Delete a comment.
  2311. *
  2312. * By default, the comment will be moved to the trash instead of deleted.
  2313. * See {@link wp_delete_comment()} for more information on
  2314. * this behavior.
  2315. *
  2316. * @since 2.7.0
  2317. *
  2318. * @param array $args Method parameters. Contains:
  2319. * - blog_id
  2320. * - username
  2321. * - password
  2322. * - comment_id
  2323. * @return mixed {@link wp_delete_comment()}
  2324. */
  2325. function wp_deleteComment($args) {
  2326. $this->escape($args);
  2327. $blog_id = (int) $args[0];
  2328. $username = $args[1];
  2329. $password = $args[2];
  2330. $comment_ID = (int) $args[3];
  2331. if ( !$user = $this->login($username, $password) )
  2332. return $this->error;
  2333. if ( !current_user_can( 'moderate_comments' ) )
  2334. return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
  2335. if ( ! get_comment($comment_ID) )
  2336. return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
  2337. if ( !current_user_can( 'edit_comment', $comment_ID ) )
  2338. return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
  2339. do_action('xmlrpc_call', 'wp.deleteComment');
  2340. $status = wp_delete_comment( $comment_ID );
  2341. if( true == $status )
  2342. do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_ID, $args );
  2343. return $status;
  2344. }
  2345. /**
  2346. * Edit comment.
  2347. *
  2348. * Besides the common blog_id, username, and password arguments, it takes a
  2349. * comment_id integer and a content_struct array as last argument.
  2350. *
  2351. * The allowed keys in the content_struct array are:
  2352. * - 'author'
  2353. * - 'author_url'
  2354. * - 'author_email'
  2355. * - 'content'
  2356. * - 'date_created_gmt'
  2357. * - 'status'. Common statuses are 'approve', 'hold', 'spam'. See {@link get_comment_statuses()} for more details
  2358. *
  2359. * @since 2.7.0
  2360. *
  2361. * @param array $args. Contains:
  2362. * - blog_id
  2363. * - username
  2364. * - password
  2365. * - comment_id
  2366. * - content_struct
  2367. * @return bool True, on success.
  2368. */
  2369. function wp_editComment($args) {
  2370. $this->escape($args);
  2371. $blog_id = (int) $args[0];
  2372. $username = $args[1];
  2373. $password = $args[2];
  2374. $comment_ID = (int) $args[3];
  2375. $content_struct = $args[4];
  2376. if ( !$user = $this->login($username, $password) )
  2377. return $this->error;
  2378. if ( !current_user_can( 'moderate_comments' ) )
  2379. return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
  2380. if ( ! get_comment($comment_ID) )
  2381. return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
  2382. if ( !current_user_can( 'edit_comment', $comment_ID ) )
  2383. return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
  2384. do_action('xmlrpc_call', 'wp.editComment');
  2385. if ( isset($content_struct['status']) ) {
  2386. $statuses = get_comment_statuses();
  2387. $statuses = array_keys($statuses);
  2388. if ( ! in_array($content_struct['status'], $statuses) )
  2389. return new IXR_Error( 401, __( 'Invalid comment status.' ) );
  2390. $comment_approved = $content_struct['status'];
  2391. }
  2392. // Do some timestamp voodoo
  2393. if ( !empty( $content_struct['date_created_gmt'] ) ) {
  2394. // We know this is supposed to be GMT, so we're going to slap that Z on there by force
  2395. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
  2396. $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
  2397. $comment_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
  2398. }
  2399. if ( isset($content_struct['content']) )
  2400. $comment_content = $content_struct['content'];
  2401. if ( isset($content_struct['author']) )
  2402. $comment_author = $content_struct['author'];
  2403. if ( isset($content_struct['author_url']) )
  2404. $comment_author_url = $content_struct['author_url'];
  2405. if ( isset($content_struct['author_email']) )
  2406. $comment_author_email = $content_struct['author_email'];
  2407. // We've got all the data -- post it:
  2408. $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');
  2409. $result = wp_update_comment($comment);
  2410. if ( is_wp_error( $result ) )
  2411. return new IXR_Error(500, $result->get_error_message());
  2412. if ( !$result )
  2413. return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));
  2414. do_action( 'xmlrpc_call_success_wp_editComment', $comment_ID, $args );
  2415. return true;
  2416. }
  2417. /**
  2418. * Create new comment.
  2419. *
  2420. * @since 2.7.0
  2421. *
  2422. * @param array $args Method parameters.
  2423. * @return mixed {@link wp_new_comment()}
  2424. */
  2425. function wp_newComment($args) {
  2426. global $wpdb;
  2427. $this->escape($args);
  2428. $blog_id = (int) $args[0];
  2429. $username = $args[1];
  2430. $password = $args[2];
  2431. $post = $args[3];
  2432. $content_struct = $args[4];
  2433. $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);
  2434. $user = $this->login($username, $password);
  2435. if ( !$user ) {
  2436. $logged_in = false;
  2437. if ( $allow_anon && get_option('comment_registration') )
  2438. return new IXR_Error( 403, __( 'You must be registered to comment' ) );
  2439. else if ( !$allow_anon )
  2440. return $this->error;
  2441. } else {
  2442. $logged_in = true;
  2443. }
  2444. if ( is_numeric($post) )
  2445. $post_id = absint($post);
  2446. else
  2447. $post_id = url_to_postid($post);
  2448. if ( ! $post_id )
  2449. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  2450. if ( ! get_post($post_id) )
  2451. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  2452. $comment['comment_post_ID'] = $post_id;
  2453. if ( $logged_in ) {
  2454. $comment['comment_author'] = $wpdb->escape( $user->display_name );
  2455. $comment['comment_author_email'] = $wpdb->escape( $user->user_email );
  2456. $comment['comment_author_url'] = $wpdb->escape( $user->user_url );
  2457. $comment['user_ID'] = $user->ID;
  2458. } else {
  2459. $comment['comment_author'] = '';
  2460. if ( isset($content_struct['author']) )
  2461. $comment['comment_author'] = $content_struct['author'];
  2462. $comment['comment_author_email'] = '';
  2463. if ( isset($content_struct['author_email']) )
  2464. $comment['comment_author_email'] = $content_struct['author_email'];
  2465. $comment['comment_author_url'] = '';
  2466. if ( isset($content_struct['author_url']) )
  2467. $comment['comment_author_url'] = $content_struct['author_url'];
  2468. $comment['user_ID'] = 0;
  2469. if ( get_option('require_name_email') ) {
  2470. if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )
  2471. return new IXR_Error( 403, __( 'Comment author name and email are required' ) );
  2472. elseif ( !is_email($comment['comment_author_email']) )
  2473. return new IXR_Error( 403, __( 'A valid email address is required' ) );
  2474. }
  2475. }
  2476. $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;
  2477. $comment['comment_content'] = isset($content_struct['content']) ? $content_struct['content'] : null;
  2478. do_action('xmlrpc_call', 'wp.newComment');
  2479. $comment_ID = wp_new_comment( $comment );
  2480. do_action( 'xmlrpc_call_success_wp_newComment', $comment_ID, $args );
  2481. return $comment_ID;
  2482. }
  2483. /**
  2484. * Retrieve all of the comment status.
  2485. *
  2486. * @since 2.7.0
  2487. *
  2488. * @param array $args Method parameters.
  2489. * @return array
  2490. */
  2491. function wp_getCommentStatusList($args) {
  2492. $this->escape( $args );
  2493. $blog_id = (int) $args[0];
  2494. $username = $args[1];
  2495. $password = $args[2];
  2496. if ( !$user = $this->login($username, $password) )
  2497. return $this->error;
  2498. if ( !current_user_can( 'moderate_comments' ) )
  2499. return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
  2500. do_action('xmlrpc_call', 'wp.getCommentStatusList');
  2501. return get_comment_statuses();
  2502. }
  2503. /**
  2504. * Retrieve comment count.
  2505. *
  2506. * @since 2.5.0
  2507. *
  2508. * @param array $args Method parameters.
  2509. * @return array
  2510. */
  2511. function wp_getCommentCount( $args ) {
  2512. $this->escape($args);
  2513. $blog_id = (int) $args[0];
  2514. $username = $args[1];
  2515. $password = $args[2];
  2516. $post_id = (int) $args[3];
  2517. if ( !$user = $this->login($username, $password) )
  2518. return $this->error;
  2519. if ( !current_user_can( 'edit_posts' ) )
  2520. return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );
  2521. do_action('xmlrpc_call', 'wp.getCommentCount');
  2522. $count = wp_count_comments( $post_id );
  2523. return array(
  2524. 'approved' => $count->approved,
  2525. 'awaiting_moderation' => $count->moderated,
  2526. 'spam' => $count->spam,
  2527. 'total_comments' => $count->total_comments
  2528. );
  2529. }
  2530. /**
  2531. * Retrieve post statuses.
  2532. *
  2533. * @since 2.5.0
  2534. *
  2535. * @param array $args Method parameters.
  2536. * @return array
  2537. */
  2538. function wp_getPostStatusList( $args ) {
  2539. $this->escape( $args );
  2540. $blog_id = (int) $args[0];
  2541. $username = $args[1];
  2542. $password = $args[2];
  2543. if ( !$user = $this->login($username, $password) )
  2544. return $this->error;
  2545. if ( !current_user_can( 'edit_posts' ) )
  2546. return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
  2547. do_action('xmlrpc_call', 'wp.getPostStatusList');
  2548. return get_post_statuses();
  2549. }
  2550. /**
  2551. * Retrieve page statuses.
  2552. *
  2553. * @since 2.5.0
  2554. *
  2555. * @param array $args Method parameters.
  2556. * @return array
  2557. */
  2558. function wp_getPageStatusList( $args ) {
  2559. $this->escape( $args );
  2560. $blog_id = (int) $args[0];
  2561. $username = $args[1];
  2562. $password = $args[2];
  2563. if ( !$user = $this->login($username, $password) )
  2564. return $this->error;
  2565. if ( !current_user_can( 'edit_pages' ) )
  2566. return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
  2567. do_action('xmlrpc_call', 'wp.getPageStatusList');
  2568. return get_page_statuses();
  2569. }
  2570. /**
  2571. * Retrieve page templates.
  2572. *
  2573. * @since 2.6.0
  2574. *
  2575. * @param array $args Method parameters.
  2576. * @return array
  2577. */
  2578. function wp_getPageTemplates( $args ) {
  2579. $this->escape( $args );
  2580. $blog_id = (int) $args[0];
  2581. $username = $args[1];
  2582. $password = $args[2];
  2583. if ( !$user = $this->login($username, $password) )
  2584. return $this->error;
  2585. if ( !current_user_can( 'edit_pages' ) )
  2586. return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
  2587. $templates = get_page_templates();
  2588. $templates['Default'] = 'default';
  2589. return $templates;
  2590. }
  2591. /**
  2592. * Retrieve blog options.
  2593. *
  2594. * @since 2.6.0
  2595. *
  2596. * @param array $args Method parameters.
  2597. * @return array
  2598. */
  2599. function wp_getOptions( $args ) {
  2600. $this->escape( $args );
  2601. $blog_id = (int) $args[0];
  2602. $username = $args[1];
  2603. $password = $args[2];
  2604. $options = isset( $args[3] ) ? (array) $args[3] : array();
  2605. if ( !$user = $this->login($username, $password) )
  2606. return $this->error;
  2607. // If no specific options where asked for, return all of them
  2608. if ( count( $options ) == 0 )
  2609. $options = array_keys($this->blog_options);
  2610. return $this->_getOptions($options);
  2611. }
  2612. /**
  2613. * Retrieve blog options value from list.
  2614. *
  2615. * @since 2.6.0
  2616. *
  2617. * @param array $options Options to retrieve.
  2618. * @return array
  2619. */
  2620. function _getOptions($options) {
  2621. $data = array();
  2622. foreach ( $options as $option ) {
  2623. if ( array_key_exists( $option, $this->blog_options ) ) {
  2624. $data[$option] = $this->blog_options[$option];
  2625. //Is the value static or dynamic?
  2626. if ( isset( $data[$option]['option'] ) ) {
  2627. $data[$option]['value'] = get_option( $data[$option]['option'] );
  2628. unset($data[$option]['option']);
  2629. }
  2630. }
  2631. }
  2632. return $data;
  2633. }
  2634. /**
  2635. * Update blog options.
  2636. *
  2637. * @since 2.6.0
  2638. *
  2639. * @param array $args Method parameters.
  2640. * @return unknown
  2641. */
  2642. function wp_setOptions( $args ) {
  2643. $this->escape( $args );
  2644. $blog_id = (int) $args[0];
  2645. $username = $args[1];
  2646. $password = $args[2];
  2647. $options = (array) $args[3];
  2648. if ( !$user = $this->login($username, $password) )
  2649. return $this->error;
  2650. if ( !current_user_can( 'manage_options' ) )
  2651. return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );
  2652. foreach ( $options as $o_name => $o_value ) {
  2653. $option_names[] = $o_name;
  2654. if ( !array_key_exists( $o_name, $this->blog_options ) )
  2655. continue;
  2656. if ( $this->blog_options[$o_name]['readonly'] == true )
  2657. continue;
  2658. update_option( $this->blog_options[$o_name]['option'], $o_value );
  2659. }
  2660. //Now return the updated values
  2661. return $this->_getOptions($option_names);
  2662. }
  2663. /**
  2664. * Retrieve a media item by ID
  2665. *
  2666. * @since 3.1.0
  2667. *
  2668. * @param array $args Method parameters. Contains:
  2669. * - blog_id
  2670. * - username
  2671. * - password
  2672. * - attachment_id
  2673. * @return array. Associative array containing:
  2674. * - 'date_created_gmt'
  2675. * - 'parent'
  2676. * - 'link'
  2677. * - 'thumbnail'
  2678. * - 'title'
  2679. * - 'caption'
  2680. * - 'description'
  2681. * - 'metadata'
  2682. */
  2683. function wp_getMediaItem($args) {
  2684. $this->escape($args);
  2685. $blog_id = (int) $args[0];
  2686. $username = $args[1];
  2687. $password = $args[2];
  2688. $attachment_id = (int) $args[3];
  2689. if ( !$user = $this->login($username, $password) )
  2690. return $this->error;
  2691. if ( !current_user_can( 'upload_files' ) )
  2692. return new IXR_Error( 403, __( 'You do not have permission to upload files.' ) );
  2693. do_action('xmlrpc_call', 'wp.getMediaItem');
  2694. if ( ! $attachment = get_post($attachment_id) )
  2695. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
  2696. return $this->_prepare_media_item( $attachment );
  2697. }
  2698. /**
  2699. * Retrieves a collection of media library items (or attachments)
  2700. *
  2701. * Besides the common blog_id, username, and password arguments, it takes a filter
  2702. * array as last argument.
  2703. *
  2704. * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'.
  2705. *
  2706. * The defaults are as follows:
  2707. * - 'number' - Default is 5. Total number of media items to retrieve.
  2708. * - 'offset' - Default is 0. See {@link WP_Query::query()} for more.
  2709. * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items.
  2710. * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf')
  2711. *
  2712. * @since 3.1.0
  2713. *
  2714. * @param array $args Method parameters. Contains:
  2715. * - blog_id
  2716. * - username
  2717. * - password
  2718. * - filter
  2719. * @return array. Contains a collection of media items. See {@link wp_xmlrpc_server::wp_getMediaItem()} for a description of each item contents
  2720. */
  2721. function wp_getMediaLibrary($args) {
  2722. $this->escape($args);
  2723. $blog_id = (int) $args[0];
  2724. $username = $args[1];
  2725. $password = $args[2];
  2726. $struct = isset( $args[3] ) ? $args[3] : array() ;
  2727. if ( !$user = $this->login($username, $password) )
  2728. return $this->error;
  2729. if ( !current_user_can( 'upload_files' ) )
  2730. return new IXR_Error( 401, __( 'You do not have permission to upload files.' ) );
  2731. do_action('xmlrpc_call', 'wp.getMediaLibrary');
  2732. $parent_id = ( isset($struct['parent_id']) ) ? absint($struct['parent_id']) : '' ;
  2733. $mime_type = ( isset($struct['mime_type']) ) ? $struct['mime_type'] : '' ;
  2734. $offset = ( isset($struct['offset']) ) ? absint($struct['offset']) : 0 ;
  2735. $number = ( isset($struct['number']) ) ? absint($struct['number']) : -1 ;
  2736. $attachments = get_posts( array('post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type ) );
  2737. $attachments_struct = array();
  2738. foreach ($attachments as $attachment )
  2739. $attachments_struct[] = $this->_prepare_media_item( $attachment );
  2740. return $attachments_struct;
  2741. }
  2742. /**
  2743. * Retrieves a list of post formats used by the site
  2744. *
  2745. * @since 3.1
  2746. *
  2747. * @param array $args Method parameters. Contains:
  2748. * - blog_id
  2749. * - username
  2750. * - password
  2751. * @return array
  2752. */
  2753. function wp_getPostFormats( $args ) {
  2754. $this->escape( $args );
  2755. $blog_id = (int) $args[0];
  2756. $username = $args[1];
  2757. $password = $args[2];
  2758. if ( !$user = $this->login( $username, $password ) )
  2759. return $this->error;
  2760. if ( !current_user_can( 'edit_posts' ) )
  2761. return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
  2762. do_action( 'xmlrpc_call', 'wp.getPostFormats' );
  2763. $formats = get_post_format_strings();
  2764. # find out if they want a list of currently supports formats
  2765. if ( isset( $args[3] ) && is_array( $args[3] ) ) {
  2766. if ( $args[3]['show-supported'] ) {
  2767. if ( current_theme_supports( 'post-formats' ) ) {
  2768. $supported = get_theme_support( 'post-formats' );
  2769. $data['all'] = $formats;
  2770. $data['supported'] = $supported[0];
  2771. $formats = $data;
  2772. }
  2773. }
  2774. }
  2775. return $formats;
  2776. }
  2777. /**
  2778. * Retrieves a post type
  2779. *
  2780. * @since 3.4.0
  2781. *
  2782. * @uses get_post_type_object()
  2783. * @param array $args Method parameters. Contains:
  2784. * - int $blog_id
  2785. * - string $username
  2786. * - string $password
  2787. * - string $post_type_name
  2788. * - array $fields
  2789. * @return array contains:
  2790. * - 'labels'
  2791. * - 'description'
  2792. * - 'capability_type'
  2793. * - 'cap'
  2794. * - 'map_meta_cap'
  2795. * - 'hierarchical'
  2796. * - 'menu_position'
  2797. * - 'taxonomies'
  2798. * - 'supports'
  2799. */
  2800. function wp_getPostType( $args ) {
  2801. if ( ! $this->minimum_args( $args, 4 ) )
  2802. return $this->error;
  2803. $this->escape( $args );
  2804. $blog_id = (int) $args[0];
  2805. $username = $args[1];
  2806. $password = $args[2];
  2807. $post_type_name = $args[3];
  2808. if ( isset( $args[4] ) )
  2809. $fields = $args[4];
  2810. else
  2811. $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' );
  2812. if ( !$user = $this->login( $username, $password ) )
  2813. return $this->error;
  2814. do_action( 'xmlrpc_call', 'wp.getPostType' );
  2815. if( ! post_type_exists( $post_type_name ) )
  2816. return new IXR_Error( 403, __( 'Invalid post type' ) );
  2817. $post_type = get_post_type_object( $post_type_name );
  2818. if( ! current_user_can( $post_type->cap->edit_posts ) )
  2819. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post type.' ) );
  2820. return $this->_prepare_post_type( $post_type, $fields );
  2821. }
  2822. /**
  2823. * Retrieves a post types
  2824. *
  2825. * @since 3.4.0
  2826. *
  2827. * @uses get_post_types()
  2828. * @param array $args Method parameters. Contains:
  2829. * - int $blog_id
  2830. * - string $username
  2831. * - string $password
  2832. * - array $filter
  2833. * - array $fields
  2834. * @return array
  2835. */
  2836. function wp_getPostTypes( $args ) {
  2837. if ( ! $this->minimum_args( $args, 3 ) )
  2838. return $this->error;
  2839. $this->escape( $args );
  2840. $blog_id = (int) $args[0];
  2841. $username = $args[1];
  2842. $password = $args[2];
  2843. $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true );
  2844. if ( isset( $args[4] ) )
  2845. $fields = $args[4];
  2846. else
  2847. $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' );
  2848. if ( ! $user = $this->login( $username, $password ) )
  2849. return $this->error;
  2850. do_action( 'xmlrpc_call', 'wp.getPostTypes' );
  2851. $post_types = get_post_types( $filter, 'objects' );
  2852. $struct = array();
  2853. foreach( $post_types as $post_type ) {
  2854. if( ! current_user_can( $post_type->cap->edit_posts ) )
  2855. continue;
  2856. $struct[$post_type->name] = $this->_prepare_post_type( $post_type, $fields );
  2857. }
  2858. return $struct;
  2859. }
  2860. /**
  2861. * Retrieve revisions for a specific post.
  2862. *
  2863. * @since 3.5.0
  2864. *
  2865. * The optional $fields parameter specifies what fields will be included
  2866. * in the response array.
  2867. *
  2868. * @uses wp_get_post_revisions()
  2869. * @see wp_getPost() for more on $fields
  2870. *
  2871. * @param array $args Method parameters. Contains:
  2872. * - int $blog_id
  2873. * - string $username
  2874. * - string $password
  2875. * - int $post_id
  2876. * - array $fields
  2877. * @return array contains a collection of posts.
  2878. */
  2879. function wp_getRevisions( $args ) {
  2880. if ( ! $this->minimum_args( $args, 4 ) )
  2881. return $this->error;
  2882. $this->escape( $args );
  2883. $blog_id = (int) $args[0];
  2884. $username = $args[1];
  2885. $password = $args[2];
  2886. $post_id = (int) $args[3];
  2887. if ( isset( $args[4] ) )
  2888. $fields = $args[4];
  2889. else
  2890. $fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' );
  2891. if ( ! $user = $this->login( $username, $password ) )
  2892. return $this->error;
  2893. do_action( 'xmlrpc_call', 'wp.getRevisions' );
  2894. if ( ! $post = get_post( $post_id ) )
  2895. return new IXR_Error( 404, __( 'Invalid post ID' ) );
  2896. if ( ! current_user_can( 'edit_post', $post_id ) )
  2897. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
  2898. // Check if revisions are enabled.
  2899. if ( ! WP_POST_REVISIONS || ! post_type_supports( $post->post_type, 'revisions' ) )
  2900. return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) );
  2901. $revisions = wp_get_post_revisions( $post_id );
  2902. if ( ! $revisions )
  2903. return array();
  2904. $struct = array();
  2905. foreach ( $revisions as $revision ) {
  2906. if ( ! current_user_can( 'read_post', $revision->ID ) )
  2907. continue;
  2908. // Skip autosaves
  2909. if ( wp_is_post_autosave( $revision ) )
  2910. continue;
  2911. $struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields );
  2912. }
  2913. return $struct;
  2914. }
  2915. /**
  2916. * Restore a post revision
  2917. *
  2918. * @since 3.5.0
  2919. *
  2920. * @uses wp_restore_post_revision()
  2921. *
  2922. * @param array $args Method parameters. Contains:
  2923. * - int $blog_id
  2924. * - string $username
  2925. * - string $password
  2926. * - int $post_id
  2927. * @return bool false if there was an error restoring, true if success.
  2928. */
  2929. function wp_restoreRevision( $args ) {
  2930. if ( ! $this->minimum_args( $args, 3 ) )
  2931. return $this->error;
  2932. $this->escape( $args );
  2933. $blog_id = (int) $args[0];
  2934. $username = $args[1];
  2935. $password = $args[2];
  2936. $revision_id = (int) $args[3];
  2937. if ( ! $user = $this->login( $username, $password ) )
  2938. return $this->error;
  2939. do_action( 'xmlrpc_call', 'wp.restoreRevision' );
  2940. if ( ! $revision = wp_get_post_revision( $revision_id ) )
  2941. return new IXR_Error( 404, __( 'Invalid post ID' ) );
  2942. if ( wp_is_post_autosave( $revision ) )
  2943. return new IXR_Error( 404, __( 'Invalid post ID' ) );
  2944. if ( ! $post = get_post( $revision->post_parent ) )
  2945. return new IXR_Error( 404, __( 'Invalid post ID' ) );
  2946. if ( ! current_user_can( 'edit_post', $revision->post_parent ) )
  2947. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  2948. // Check if revisions are disabled.
  2949. if ( ! WP_POST_REVISIONS || ! post_type_supports( $post->post_type, 'revisions' ) )
  2950. return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) );
  2951. $post = wp_restore_post_revision( $revision_id );
  2952. return (bool) $post;
  2953. }
  2954. /* Blogger API functions.
  2955. * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
  2956. */
  2957. /**
  2958. * Retrieve blogs that user owns.
  2959. *
  2960. * Will make more sense once we support multiple blogs.
  2961. *
  2962. * @since 1.5.0
  2963. *
  2964. * @param array $args Method parameters.
  2965. * @return array
  2966. */
  2967. function blogger_getUsersBlogs($args) {
  2968. if ( is_multisite() )
  2969. return $this->_multisite_getUsersBlogs($args);
  2970. $this->escape($args);
  2971. $username = $args[1];
  2972. $password = $args[2];
  2973. if ( !$user = $this->login($username, $password) )
  2974. return $this->error;
  2975. do_action('xmlrpc_call', 'blogger.getUsersBlogs');
  2976. $is_admin = current_user_can('manage_options');
  2977. $struct = array(
  2978. 'isAdmin' => $is_admin,
  2979. 'url' => get_option('home') . '/',
  2980. 'blogid' => '1',
  2981. 'blogName' => get_option('blogname'),
  2982. 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),
  2983. );
  2984. return array($struct);
  2985. }
  2986. /**
  2987. * Private function for retrieving a users blogs for multisite setups
  2988. *
  2989. * @access protected
  2990. */
  2991. function _multisite_getUsersBlogs($args) {
  2992. $current_blog = get_blog_details();
  2993. $domain = $current_blog->domain;
  2994. $path = $current_blog->path . 'xmlrpc.php';
  2995. $rpc = new IXR_Client( set_url_scheme( "http://{$domain}{$path}" ) );
  2996. $rpc->query('wp.getUsersBlogs', $args[1], $args[2]);
  2997. $blogs = $rpc->getResponse();
  2998. if ( isset($blogs['faultCode']) )
  2999. return new IXR_Error($blogs['faultCode'], $blogs['faultString']);
  3000. if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) {
  3001. return $blogs;
  3002. } else {
  3003. foreach ( (array) $blogs as $blog ) {
  3004. if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) )
  3005. return array($blog);
  3006. }
  3007. return array();
  3008. }
  3009. }
  3010. /**
  3011. * Retrieve user's data.
  3012. *
  3013. * Gives your client some info about you, so you don't have to.
  3014. *
  3015. * @since 1.5.0
  3016. *
  3017. * @param array $args Method parameters.
  3018. * @return array
  3019. */
  3020. function blogger_getUserInfo($args) {
  3021. $this->escape($args);
  3022. $username = $args[1];
  3023. $password = $args[2];
  3024. if ( !$user = $this->login($username, $password) )
  3025. return $this->error;
  3026. if ( !current_user_can( 'edit_posts' ) )
  3027. return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) );
  3028. do_action('xmlrpc_call', 'blogger.getUserInfo');
  3029. $struct = array(
  3030. 'nickname' => $user->nickname,
  3031. 'userid' => $user->ID,
  3032. 'url' => $user->user_url,
  3033. 'lastname' => $user->last_name,
  3034. 'firstname' => $user->first_name
  3035. );
  3036. return $struct;
  3037. }
  3038. /**
  3039. * Retrieve post.
  3040. *
  3041. * @since 1.5.0
  3042. *
  3043. * @param array $args Method parameters.
  3044. * @return array
  3045. */
  3046. function blogger_getPost($args) {
  3047. $this->escape($args);
  3048. $post_ID = (int) $args[1];
  3049. $username = $args[2];
  3050. $password = $args[3];
  3051. if ( !$user = $this->login($username, $password) )
  3052. return $this->error;
  3053. $post_data = get_post($post_ID, ARRAY_A);
  3054. if ( ! $post_data )
  3055. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  3056. if ( !current_user_can( 'edit_post', $post_ID ) )
  3057. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  3058. do_action('xmlrpc_call', 'blogger.getPost');
  3059. $categories = implode(',', wp_get_post_categories($post_ID));
  3060. $content = '<title>'.stripslashes($post_data['post_title']).'</title>';
  3061. $content .= '<category>'.$categories.'</category>';
  3062. $content .= stripslashes($post_data['post_content']);
  3063. $struct = array(
  3064. 'userid' => $post_data['post_author'],
  3065. 'dateCreated' => $this->_convert_date( $post_data['post_date'] ),
  3066. 'content' => $content,
  3067. 'postid' => (string) $post_data['ID']
  3068. );
  3069. return $struct;
  3070. }
  3071. /**
  3072. * Retrieve list of recent posts.
  3073. *
  3074. * @since 1.5.0
  3075. *
  3076. * @param array $args Method parameters.
  3077. * @return array
  3078. */
  3079. function blogger_getRecentPosts($args) {
  3080. $this->escape($args);
  3081. // $args[0] = appkey - ignored
  3082. $blog_ID = (int) $args[1]; /* though we don't use it yet */
  3083. $username = $args[2];
  3084. $password = $args[3];
  3085. if ( isset( $args[4] ) )
  3086. $query = array( 'numberposts' => absint( $args[4] ) );
  3087. else
  3088. $query = array();
  3089. if ( !$user = $this->login($username, $password) )
  3090. return $this->error;
  3091. do_action('xmlrpc_call', 'blogger.getRecentPosts');
  3092. $posts_list = wp_get_recent_posts( $query );
  3093. if ( !$posts_list ) {
  3094. $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
  3095. return $this->error;
  3096. }
  3097. foreach ($posts_list as $entry) {
  3098. if ( !current_user_can( 'edit_post', $entry['ID'] ) )
  3099. continue;
  3100. $post_date = $this->_convert_date( $entry['post_date'] );
  3101. $categories = implode(',', wp_get_post_categories($entry['ID']));
  3102. $content = '<title>'.stripslashes($entry['post_title']).'</title>';
  3103. $content .= '<category>'.$categories.'</category>';
  3104. $content .= stripslashes($entry['post_content']);
  3105. $struct[] = array(
  3106. 'userid' => $entry['post_author'],
  3107. 'dateCreated' => $post_date,
  3108. 'content' => $content,
  3109. 'postid' => (string) $entry['ID'],
  3110. );
  3111. }
  3112. $recent_posts = array();
  3113. for ( $j=0; $j<count($struct); $j++ ) {
  3114. array_push($recent_posts, $struct[$j]);
  3115. }
  3116. return $recent_posts;
  3117. }
  3118. /**
  3119. * Deprecated.
  3120. *
  3121. * @since 1.5.0
  3122. * @deprecated 3.5.0
  3123. */
  3124. function blogger_getTemplate($args) {
  3125. return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) );
  3126. }
  3127. /**
  3128. * Deprecated.
  3129. *
  3130. * @since 1.5.0
  3131. * @deprecated 3.5.0
  3132. */
  3133. function blogger_setTemplate($args) {
  3134. return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) );
  3135. }
  3136. /**
  3137. * Create new post.
  3138. *
  3139. * @since 1.5.0
  3140. *
  3141. * @param array $args Method parameters.
  3142. * @return int
  3143. */
  3144. function blogger_newPost($args) {
  3145. $this->escape($args);
  3146. $blog_ID = (int) $args[1]; /* though we don't use it yet */
  3147. $username = $args[2];
  3148. $password = $args[3];
  3149. $content = $args[4];
  3150. $publish = $args[5];
  3151. if ( !$user = $this->login($username, $password) )
  3152. return $this->error;
  3153. do_action('xmlrpc_call', 'blogger.newPost');
  3154. $cap = ($publish) ? 'publish_posts' : 'edit_posts';
  3155. if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || !current_user_can($cap) )
  3156. return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));
  3157. $post_status = ($publish) ? 'publish' : 'draft';
  3158. $post_author = $user->ID;
  3159. $post_title = xmlrpc_getposttitle($content);
  3160. $post_category = xmlrpc_getpostcategory($content);
  3161. $post_content = xmlrpc_removepostdata($content);
  3162. $post_date = current_time('mysql');
  3163. $post_date_gmt = current_time('mysql', 1);
  3164. $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
  3165. $post_ID = wp_insert_post($post_data);
  3166. if ( is_wp_error( $post_ID ) )
  3167. return new IXR_Error(500, $post_ID->get_error_message());
  3168. if ( !$post_ID )
  3169. return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
  3170. $this->attach_uploads( $post_ID, $post_content );
  3171. do_action( 'xmlrpc_call_success_blogger_newPost', $post_ID, $args );
  3172. return $post_ID;
  3173. }
  3174. /**
  3175. * Edit a post.
  3176. *
  3177. * @since 1.5.0
  3178. *
  3179. * @param array $args Method parameters.
  3180. * @return bool true when done.
  3181. */
  3182. function blogger_editPost($args) {
  3183. $this->escape($args);
  3184. $post_ID = (int) $args[1];
  3185. $username = $args[2];
  3186. $password = $args[3];
  3187. $content = $args[4];
  3188. $publish = $args[5];
  3189. if ( !$user = $this->login($username, $password) )
  3190. return $this->error;
  3191. do_action('xmlrpc_call', 'blogger.editPost');
  3192. $actual_post = get_post($post_ID,ARRAY_A);
  3193. if ( !$actual_post || $actual_post['post_type'] != 'post' )
  3194. return new IXR_Error(404, __('Sorry, no such post.'));
  3195. $this->escape($actual_post);
  3196. if ( !current_user_can('edit_post', $post_ID) )
  3197. return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));
  3198. extract($actual_post, EXTR_SKIP);
  3199. if ( ('publish' == $post_status) && !current_user_can('publish_posts') )
  3200. return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
  3201. $post_title = xmlrpc_getposttitle($content);
  3202. $post_category = xmlrpc_getpostcategory($content);
  3203. $post_content = xmlrpc_removepostdata($content);
  3204. $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
  3205. $result = wp_update_post($postdata);
  3206. if ( !$result )
  3207. return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));
  3208. $this->attach_uploads( $ID, $post_content );
  3209. do_action( 'xmlrpc_call_success_blogger_editPost', $post_ID, $args );
  3210. return true;
  3211. }
  3212. /**
  3213. * Remove a post.
  3214. *
  3215. * @since 1.5.0
  3216. *
  3217. * @param array $args Method parameters.
  3218. * @return bool True when post is deleted.
  3219. */
  3220. function blogger_deletePost($args) {
  3221. $this->escape($args);
  3222. $post_ID = (int) $args[1];
  3223. $username = $args[2];
  3224. $password = $args[3];
  3225. $publish = $args[4];
  3226. if ( !$user = $this->login($username, $password) )
  3227. return $this->error;
  3228. do_action('xmlrpc_call', 'blogger.deletePost');
  3229. $actual_post = get_post($post_ID,ARRAY_A);
  3230. if ( !$actual_post || $actual_post['post_type'] != 'post' )
  3231. return new IXR_Error(404, __('Sorry, no such post.'));
  3232. if ( !current_user_can('delete_post', $post_ID) )
  3233. return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));
  3234. $result = wp_delete_post($post_ID);
  3235. if ( !$result )
  3236. return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));
  3237. do_action( 'xmlrpc_call_success_blogger_deletePost', $post_ID, $args );
  3238. return true;
  3239. }
  3240. /* MetaWeblog API functions
  3241. * specs on wherever Dave Winer wants them to be
  3242. */
  3243. /**
  3244. * Create a new post.
  3245. *
  3246. * The 'content_struct' argument must contain:
  3247. * - title
  3248. * - description
  3249. * - mt_excerpt
  3250. * - mt_text_more
  3251. * - mt_keywords
  3252. * - mt_tb_ping_urls
  3253. * - categories
  3254. *
  3255. * Also, it can optionally contain:
  3256. * - wp_slug
  3257. * - wp_password
  3258. * - wp_page_parent_id
  3259. * - wp_page_order
  3260. * - wp_author_id
  3261. * - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending'
  3262. * - mt_allow_comments - can be 'open' or 'closed'
  3263. * - mt_allow_pings - can be 'open' or 'closed'
  3264. * - date_created_gmt
  3265. * - dateCreated
  3266. * - wp_post_thumbnail
  3267. *
  3268. * @since 1.5.0
  3269. *
  3270. * @param array $args Method parameters. Contains:
  3271. * - blog_id
  3272. * - username
  3273. * - password
  3274. * - content_struct
  3275. * - publish
  3276. * @return int
  3277. */
  3278. function mw_newPost($args) {
  3279. $this->escape($args);
  3280. $blog_ID = (int) $args[0];
  3281. $username = $args[1];
  3282. $password = $args[2];
  3283. $content_struct = $args[3];
  3284. $publish = isset( $args[4] ) ? $args[4] : 0;
  3285. if ( !$user = $this->login($username, $password) )
  3286. return $this->error;
  3287. do_action('xmlrpc_call', 'metaWeblog.newPost');
  3288. $page_template = '';
  3289. if ( !empty( $content_struct['post_type'] ) ) {
  3290. if ( $content_struct['post_type'] == 'page' ) {
  3291. if ( $publish )
  3292. $cap = 'publish_pages';
  3293. elseif ( isset( $content_struct['page_status'] ) && 'publish' == $content_struct['page_status'] )
  3294. $cap = 'publish_pages';
  3295. else
  3296. $cap = 'edit_pages';
  3297. $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
  3298. $post_type = 'page';
  3299. if ( !empty( $content_struct['wp_page_template'] ) )
  3300. $page_template = $content_struct['wp_page_template'];
  3301. } elseif ( $content_struct['post_type'] == 'post' ) {
  3302. if ( $publish )
  3303. $cap = 'publish_posts';
  3304. elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'] )
  3305. $cap = 'publish_posts';
  3306. else
  3307. $cap = 'edit_posts';
  3308. $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
  3309. $post_type = 'post';
  3310. } else {
  3311. // No other post_type values are allowed here
  3312. return new IXR_Error( 401, __( 'Invalid post type' ) );
  3313. }
  3314. } else {
  3315. if ( $publish )
  3316. $cap = 'publish_posts';
  3317. elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'])
  3318. $cap = 'publish_posts';
  3319. else
  3320. $cap = 'edit_posts';
  3321. $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
  3322. $post_type = 'post';
  3323. }
  3324. if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) )
  3325. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) );
  3326. if ( !current_user_can( $cap ) )
  3327. return new IXR_Error( 401, $error_message );
  3328. // Check for a valid post format if one was given
  3329. if ( isset( $content_struct['wp_post_format'] ) ) {
  3330. $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
  3331. if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
  3332. return new IXR_Error( 404, __( 'Invalid post format' ) );
  3333. }
  3334. }
  3335. // Let WordPress generate the post_name (slug) unless
  3336. // one has been provided.
  3337. $post_name = "";
  3338. if ( isset($content_struct['wp_slug']) )
  3339. $post_name = $content_struct['wp_slug'];
  3340. // Only use a password if one was given.
  3341. if ( isset($content_struct['wp_password']) )
  3342. $post_password = $content_struct['wp_password'];
  3343. // Only set a post parent if one was provided.
  3344. if ( isset($content_struct['wp_page_parent_id']) )
  3345. $post_parent = $content_struct['wp_page_parent_id'];
  3346. // Only set the menu_order if it was provided.
  3347. if ( isset($content_struct['wp_page_order']) )
  3348. $menu_order = $content_struct['wp_page_order'];
  3349. $post_author = $user->ID;
  3350. // If an author id was provided then use it instead.
  3351. if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) {
  3352. switch ( $post_type ) {
  3353. case "post":
  3354. if ( !current_user_can( 'edit_others_posts' ) )
  3355. return( new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) ) );
  3356. break;
  3357. case "page":
  3358. if ( !current_user_can( 'edit_others_pages' ) )
  3359. return( new IXR_Error( 401, __( 'You are not allowed to create pages as this user.' ) ) );
  3360. break;
  3361. default:
  3362. return( new IXR_Error( 401, __( 'Invalid post type' ) ) );
  3363. break;
  3364. }
  3365. $author = get_userdata( $content_struct['wp_author_id'] );
  3366. if ( ! $author )
  3367. return new IXR_Error( 404, __( 'Invalid author ID.' ) );
  3368. $post_author = $content_struct['wp_author_id'];
  3369. }
  3370. $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null;
  3371. $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null;
  3372. $post_status = $publish ? 'publish' : 'draft';
  3373. if ( isset( $content_struct["{$post_type}_status"] ) ) {
  3374. switch ( $content_struct["{$post_type}_status"] ) {
  3375. case 'draft':
  3376. case 'pending':
  3377. case 'private':
  3378. case 'publish':
  3379. $post_status = $content_struct["{$post_type}_status"];
  3380. break;
  3381. default:
  3382. $post_status = $publish ? 'publish' : 'draft';
  3383. break;
  3384. }
  3385. }
  3386. $post_excerpt = isset($content_struct['mt_excerpt']) ? $content_struct['mt_excerpt'] : null;
  3387. $post_more = isset($content_struct['mt_text_more']) ? $content_struct['mt_text_more'] : null;
  3388. $tags_input = isset($content_struct['mt_keywords']) ? $content_struct['mt_keywords'] : null;
  3389. if ( isset($content_struct['mt_allow_comments']) ) {
  3390. if ( !is_numeric($content_struct['mt_allow_comments']) ) {
  3391. switch ( $content_struct['mt_allow_comments'] ) {
  3392. case 'closed':
  3393. $comment_status = 'closed';
  3394. break;
  3395. case 'open':
  3396. $comment_status = 'open';
  3397. break;
  3398. default:
  3399. $comment_status = get_option('default_comment_status');
  3400. break;
  3401. }
  3402. } else {
  3403. switch ( (int) $content_struct['mt_allow_comments'] ) {
  3404. case 0:
  3405. case 2:
  3406. $comment_status = 'closed';
  3407. break;
  3408. case 1:
  3409. $comment_status = 'open';
  3410. break;
  3411. default:
  3412. $comment_status = get_option('default_comment_status');
  3413. break;
  3414. }
  3415. }
  3416. } else {
  3417. $comment_status = get_option('default_comment_status');
  3418. }
  3419. if ( isset($content_struct['mt_allow_pings']) ) {
  3420. if ( !is_numeric($content_struct['mt_allow_pings']) ) {
  3421. switch ( $content_struct['mt_allow_pings'] ) {
  3422. case 'closed':
  3423. $ping_status = 'closed';
  3424. break;
  3425. case 'open':
  3426. $ping_status = 'open';
  3427. break;
  3428. default:
  3429. $ping_status = get_option('default_ping_status');
  3430. break;
  3431. }
  3432. } else {
  3433. switch ( (int) $content_struct['mt_allow_pings'] ) {
  3434. case 0:
  3435. $ping_status = 'closed';
  3436. break;
  3437. case 1:
  3438. $ping_status = 'open';
  3439. break;
  3440. default:
  3441. $ping_status = get_option('default_ping_status');
  3442. break;
  3443. }
  3444. }
  3445. } else {
  3446. $ping_status = get_option('default_ping_status');
  3447. }
  3448. if ( $post_more )
  3449. $post_content = $post_content . '<!--more-->' . $post_more;
  3450. $to_ping = null;
  3451. if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
  3452. $to_ping = $content_struct['mt_tb_ping_urls'];
  3453. if ( is_array($to_ping) )
  3454. $to_ping = implode(' ', $to_ping);
  3455. }
  3456. // Do some timestamp voodoo
  3457. if ( !empty( $content_struct['date_created_gmt'] ) )
  3458. // We know this is supposed to be GMT, so we're going to slap that Z on there by force
  3459. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
  3460. elseif ( !empty( $content_struct['dateCreated']) )
  3461. $dateCreated = $content_struct['dateCreated']->getIso();
  3462. if ( !empty( $dateCreated ) ) {
  3463. $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
  3464. $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
  3465. } else {
  3466. $post_date = current_time('mysql');
  3467. $post_date_gmt = current_time('mysql', 1);
  3468. }
  3469. $post_category = array();
  3470. if ( isset( $content_struct['categories'] ) ) {
  3471. $catnames = $content_struct['categories'];
  3472. if ( is_array($catnames) ) {
  3473. foreach ($catnames as $cat) {
  3474. $post_category[] = get_cat_ID($cat);
  3475. }
  3476. }
  3477. }
  3478. $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template');
  3479. $post_ID = $postdata['ID'] = get_default_post_to_edit( $post_type, true )->ID;
  3480. // Only posts can be sticky
  3481. if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
  3482. if ( $content_struct['sticky'] == true )
  3483. stick_post( $post_ID );
  3484. elseif ( $content_struct['sticky'] == false )
  3485. unstick_post( $post_ID );
  3486. }
  3487. if ( isset($content_struct['custom_fields']) )
  3488. $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
  3489. if ( isset ( $content_struct['wp_post_thumbnail'] ) ) {
  3490. if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false )
  3491. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
  3492. unset( $content_struct['wp_post_thumbnail'] );
  3493. }
  3494. // Handle enclosures
  3495. $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null;
  3496. $this->add_enclosure_if_new($post_ID, $thisEnclosure);
  3497. $this->attach_uploads( $post_ID, $post_content );
  3498. // Handle post formats if assigned, value is validated earlier
  3499. // in this function
  3500. if ( isset( $content_struct['wp_post_format'] ) )
  3501. set_post_format( $post_ID, $content_struct['wp_post_format'] );
  3502. $post_ID = wp_insert_post( $postdata, true );
  3503. if ( is_wp_error( $post_ID ) )
  3504. return new IXR_Error(500, $post_ID->get_error_message());
  3505. if ( !$post_ID )
  3506. return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
  3507. do_action( 'xmlrpc_call_success_mw_newPost', $post_ID, $args );
  3508. return strval($post_ID);
  3509. }
  3510. function add_enclosure_if_new($post_ID, $enclosure) {
  3511. if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
  3512. $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'];
  3513. $found = false;
  3514. foreach ( (array) get_post_custom($post_ID) as $key => $val) {
  3515. if ($key == 'enclosure') {
  3516. foreach ( (array) $val as $enc ) {
  3517. if ($enc == $encstring) {
  3518. $found = true;
  3519. break 2;
  3520. }
  3521. }
  3522. }
  3523. }
  3524. if (!$found)
  3525. add_post_meta( $post_ID, 'enclosure', $encstring );
  3526. }
  3527. }
  3528. /**
  3529. * Attach upload to a post.
  3530. *
  3531. * @since 2.1.0
  3532. *
  3533. * @param int $post_ID Post ID.
  3534. * @param string $post_content Post Content for attachment.
  3535. */
  3536. function attach_uploads( $post_ID, $post_content ) {
  3537. global $wpdb;
  3538. // find any unattached files
  3539. $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );
  3540. if ( is_array( $attachments ) ) {
  3541. foreach ( $attachments as $file ) {
  3542. if ( strpos( $post_content, $file->guid ) !== false )
  3543. $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) );
  3544. }
  3545. }
  3546. }
  3547. /**
  3548. * Edit a post.
  3549. *
  3550. * @since 1.5.0
  3551. *
  3552. * @param array $args Method parameters.
  3553. * @return bool True on success.
  3554. */
  3555. function mw_editPost($args) {
  3556. $this->escape($args);
  3557. $post_ID = (int) $args[0];
  3558. $username = $args[1];
  3559. $password = $args[2];
  3560. $content_struct = $args[3];
  3561. $publish = isset( $args[4] ) ? $args[4] : 0;
  3562. if ( ! $user = $this->login($username, $password) )
  3563. return $this->error;
  3564. do_action('xmlrpc_call', 'metaWeblog.editPost');
  3565. $postdata = get_post( $post_ID, ARRAY_A );
  3566. // If there is no post data for the give post id, stop
  3567. // now and return an error. Other wise a new post will be
  3568. // created (which was the old behavior).
  3569. if ( ! $postdata || empty( $postdata[ 'ID' ] ) )
  3570. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  3571. if ( ! current_user_can( 'edit_post', $post_ID ) )
  3572. return new IXR_Error( 401, __( 'Sorry, you do not have the right to edit this post.' ) );
  3573. // Use wp.editPost to edit post types other than post and page.
  3574. if ( ! in_array( $postdata[ 'post_type' ], array( 'post', 'page' ) ) )
  3575. return new IXR_Error( 401, __( 'Invalid post type' ) );
  3576. // Thwart attempt to change the post type.
  3577. if ( ! empty( $content_struct[ 'post_type' ] ) && ( $content_struct['post_type'] != $postdata[ 'post_type' ] ) )
  3578. return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
  3579. // Check for a valid post format if one was given
  3580. if ( isset( $content_struct['wp_post_format'] ) ) {
  3581. $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
  3582. if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
  3583. return new IXR_Error( 404, __( 'Invalid post format' ) );
  3584. }
  3585. }
  3586. $this->escape($postdata);
  3587. extract($postdata, EXTR_SKIP);
  3588. // Let WordPress manage slug if none was provided.
  3589. $post_name = "";
  3590. $post_name = $postdata['post_name'];
  3591. if ( isset($content_struct['wp_slug']) )
  3592. $post_name = $content_struct['wp_slug'];
  3593. // Only use a password if one was given.
  3594. if ( isset($content_struct['wp_password']) )
  3595. $post_password = $content_struct['wp_password'];
  3596. // Only set a post parent if one was given.
  3597. if ( isset($content_struct['wp_page_parent_id']) )
  3598. $post_parent = $content_struct['wp_page_parent_id'];
  3599. // Only set the menu_order if it was given.
  3600. if ( isset($content_struct['wp_page_order']) )
  3601. $menu_order = $content_struct['wp_page_order'];
  3602. if ( ! empty( $content_struct['wp_page_template'] ) && 'page' == $post_type )
  3603. $page_template = $content_struct['wp_page_template'];
  3604. $post_author = $postdata['post_author'];
  3605. // Only set the post_author if one is set.
  3606. if ( isset($content_struct['wp_author_id']) && ($user->ID != $content_struct['wp_author_id']) ) {
  3607. switch ( $post_type ) {
  3608. case 'post':
  3609. if ( !current_user_can('edit_others_posts') )
  3610. return(new IXR_Error(401, __('You are not allowed to change the post author as this user.')));
  3611. break;
  3612. case 'page':
  3613. if ( !current_user_can('edit_others_pages') )
  3614. return(new IXR_Error(401, __('You are not allowed to change the page author as this user.')));
  3615. break;
  3616. default:
  3617. return(new IXR_Error(401, __('Invalid post type')));
  3618. break;
  3619. }
  3620. $post_author = $content_struct['wp_author_id'];
  3621. }
  3622. if ( isset($content_struct['mt_allow_comments']) ) {
  3623. if ( !is_numeric($content_struct['mt_allow_comments']) ) {
  3624. switch ( $content_struct['mt_allow_comments'] ) {
  3625. case 'closed':
  3626. $comment_status = 'closed';
  3627. break;
  3628. case 'open':
  3629. $comment_status = 'open';
  3630. break;
  3631. default:
  3632. $comment_status = get_option('default_comment_status');
  3633. break;
  3634. }
  3635. } else {
  3636. switch ( (int) $content_struct['mt_allow_comments'] ) {
  3637. case 0:
  3638. case 2:
  3639. $comment_status = 'closed';
  3640. break;
  3641. case 1:
  3642. $comment_status = 'open';
  3643. break;
  3644. default:
  3645. $comment_status = get_option('default_comment_status');
  3646. break;
  3647. }
  3648. }
  3649. }
  3650. if ( isset($content_struct['mt_allow_pings']) ) {
  3651. if ( !is_numeric($content_struct['mt_allow_pings']) ) {
  3652. switch ( $content_struct['mt_allow_pings'] ) {
  3653. case 'closed':
  3654. $ping_status = 'closed';
  3655. break;
  3656. case 'open':
  3657. $ping_status = 'open';
  3658. break;
  3659. default:
  3660. $ping_status = get_option('default_ping_status');
  3661. break;
  3662. }
  3663. } else {
  3664. switch ( (int) $content_struct["mt_allow_pings"] ) {
  3665. case 0:
  3666. $ping_status = 'closed';
  3667. break;
  3668. case 1:
  3669. $ping_status = 'open';
  3670. break;
  3671. default:
  3672. $ping_status = get_option('default_ping_status');
  3673. break;
  3674. }
  3675. }
  3676. }
  3677. if ( isset( $content_struct['title'] ) )
  3678. $post_title = $content_struct['title'];
  3679. if ( isset( $content_struct['description'] ) )
  3680. $post_content = $content_struct['description'];
  3681. $post_category = array();
  3682. if ( isset( $content_struct['categories'] ) ) {
  3683. $catnames = $content_struct['categories'];
  3684. if ( is_array($catnames) ) {
  3685. foreach ($catnames as $cat) {
  3686. $post_category[] = get_cat_ID($cat);
  3687. }
  3688. }
  3689. }
  3690. if ( isset( $content_struct['mt_excerpt'] ) )
  3691. $post_excerpt = $content_struct['mt_excerpt'];
  3692. $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null;
  3693. $post_status = $publish ? 'publish' : 'draft';
  3694. if ( isset( $content_struct["{$post_type}_status"] ) ) {
  3695. switch( $content_struct["{$post_type}_status"] ) {
  3696. case 'draft':
  3697. case 'pending':
  3698. case 'private':
  3699. case 'publish':
  3700. $post_status = $content_struct["{$post_type}_status"];
  3701. break;
  3702. default:
  3703. $post_status = $publish ? 'publish' : 'draft';
  3704. break;
  3705. }
  3706. }
  3707. $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null;
  3708. if ( ('publish' == $post_status) ) {
  3709. if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )
  3710. return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));
  3711. else if ( !current_user_can('publish_posts') )
  3712. return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
  3713. }
  3714. if ( $post_more )
  3715. $post_content = $post_content . "<!--more-->" . $post_more;
  3716. $to_ping = null;
  3717. if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
  3718. $to_ping = $content_struct['mt_tb_ping_urls'];
  3719. if ( is_array($to_ping) )
  3720. $to_ping = implode(' ', $to_ping);
  3721. }
  3722. // Do some timestamp voodoo
  3723. if ( !empty( $content_struct['date_created_gmt'] ) )
  3724. // We know this is supposed to be GMT, so we're going to slap that Z on there by force
  3725. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
  3726. elseif ( !empty( $content_struct['dateCreated']) )
  3727. $dateCreated = $content_struct['dateCreated']->getIso();
  3728. if ( !empty( $dateCreated ) ) {
  3729. $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
  3730. $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
  3731. } else {
  3732. $post_date = $postdata['post_date'];
  3733. $post_date_gmt = $postdata['post_date_gmt'];
  3734. }
  3735. // We've got all the data -- post it:
  3736. $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template');
  3737. $result = wp_update_post($newpost, true);
  3738. if ( is_wp_error( $result ) )
  3739. return new IXR_Error(500, $result->get_error_message());
  3740. if ( !$result )
  3741. return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));
  3742. // Only posts can be sticky
  3743. if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
  3744. if ( $content_struct['sticky'] == true )
  3745. stick_post( $post_ID );
  3746. elseif ( $content_struct['sticky'] == false )
  3747. unstick_post( $post_ID );
  3748. }
  3749. if ( isset($content_struct['custom_fields']) )
  3750. $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
  3751. if ( isset ( $content_struct['wp_post_thumbnail'] ) ) {
  3752. // empty value deletes, non-empty value adds/updates
  3753. if ( empty( $content_struct['wp_post_thumbnail'] ) ) {
  3754. delete_post_thumbnail( $post_ID );
  3755. } else {
  3756. if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false )
  3757. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
  3758. }
  3759. unset( $content_struct['wp_post_thumbnail'] );
  3760. }
  3761. // Handle enclosures
  3762. $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null;
  3763. $this->add_enclosure_if_new($post_ID, $thisEnclosure);
  3764. $this->attach_uploads( $ID, $post_content );
  3765. // Handle post formats if assigned, validation is handled
  3766. // earlier in this function
  3767. if ( isset( $content_struct['wp_post_format'] ) )
  3768. set_post_format( $post_ID, $content_struct['wp_post_format'] );
  3769. do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args );
  3770. return true;
  3771. }
  3772. /**
  3773. * Retrieve post.
  3774. *
  3775. * @since 1.5.0
  3776. *
  3777. * @param array $args Method parameters.
  3778. * @return array
  3779. */
  3780. function mw_getPost($args) {
  3781. $this->escape($args);
  3782. $post_ID = (int) $args[0];
  3783. $username = $args[1];
  3784. $password = $args[2];
  3785. if ( !$user = $this->login($username, $password) )
  3786. return $this->error;
  3787. $postdata = get_post($post_ID, ARRAY_A);
  3788. if ( ! $postdata )
  3789. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  3790. if ( !current_user_can( 'edit_post', $post_ID ) )
  3791. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  3792. do_action('xmlrpc_call', 'metaWeblog.getPost');
  3793. if ($postdata['post_date'] != '') {
  3794. $post_date = $this->_convert_date( $postdata['post_date'] );
  3795. $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] );
  3796. $post_modified = $this->_convert_date( $postdata['post_modified'] );
  3797. $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] );
  3798. $categories = array();
  3799. $catids = wp_get_post_categories($post_ID);
  3800. foreach($catids as $catid)
  3801. $categories[] = get_cat_name($catid);
  3802. $tagnames = array();
  3803. $tags = wp_get_post_tags( $post_ID );
  3804. if ( !empty( $tags ) ) {
  3805. foreach ( $tags as $tag )
  3806. $tagnames[] = $tag->name;
  3807. $tagnames = implode( ', ', $tagnames );
  3808. } else {
  3809. $tagnames = '';
  3810. }
  3811. $post = get_extended($postdata['post_content']);
  3812. $link = post_permalink($postdata['ID']);
  3813. // Get the author info.
  3814. $author = get_userdata($postdata['post_author']);
  3815. $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;
  3816. $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;
  3817. // Consider future posts as published
  3818. if ( $postdata['post_status'] === 'future' )
  3819. $postdata['post_status'] = 'publish';
  3820. // Get post format
  3821. $post_format = get_post_format( $post_ID );
  3822. if ( empty( $post_format ) )
  3823. $post_format = 'standard';
  3824. $sticky = false;
  3825. if ( is_sticky( $post_ID ) )
  3826. $sticky = true;
  3827. $enclosure = array();
  3828. foreach ( (array) get_post_custom($post_ID) as $key => $val) {
  3829. if ($key == 'enclosure') {
  3830. foreach ( (array) $val as $enc ) {
  3831. $encdata = explode("\n", $enc);
  3832. $enclosure['url'] = trim(htmlspecialchars($encdata[0]));
  3833. $enclosure['length'] = (int) trim($encdata[1]);
  3834. $enclosure['type'] = trim($encdata[2]);
  3835. break 2;
  3836. }
  3837. }
  3838. }
  3839. $resp = array(
  3840. 'dateCreated' => $post_date,
  3841. 'userid' => $postdata['post_author'],
  3842. 'postid' => $postdata['ID'],
  3843. 'description' => $post['main'],
  3844. 'title' => $postdata['post_title'],
  3845. 'link' => $link,
  3846. 'permaLink' => $link,
  3847. // commented out because no other tool seems to use this
  3848. // 'content' => $entry['post_content'],
  3849. 'categories' => $categories,
  3850. 'mt_excerpt' => $postdata['post_excerpt'],
  3851. 'mt_text_more' => $post['extended'],
  3852. 'wp_more_text' => $post['more_text'],
  3853. 'mt_allow_comments' => $allow_comments,
  3854. 'mt_allow_pings' => $allow_pings,
  3855. 'mt_keywords' => $tagnames,
  3856. 'wp_slug' => $postdata['post_name'],
  3857. 'wp_password' => $postdata['post_password'],
  3858. 'wp_author_id' => (string) $author->ID,
  3859. 'wp_author_display_name' => $author->display_name,
  3860. 'date_created_gmt' => $post_date_gmt,
  3861. 'post_status' => $postdata['post_status'],
  3862. 'custom_fields' => $this->get_custom_fields($post_ID),
  3863. 'wp_post_format' => $post_format,
  3864. 'sticky' => $sticky,
  3865. 'date_modified' => $post_modified,
  3866. 'date_modified_gmt' => $post_modified_gmt
  3867. );
  3868. if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure;
  3869. $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] );
  3870. return $resp;
  3871. } else {
  3872. return new IXR_Error(404, __('Sorry, no such post.'));
  3873. }
  3874. }
  3875. /**
  3876. * Retrieve list of recent posts.
  3877. *
  3878. * @since 1.5.0
  3879. *
  3880. * @param array $args Method parameters.
  3881. * @return array
  3882. */
  3883. function mw_getRecentPosts($args) {
  3884. $this->escape($args);
  3885. $blog_ID = (int) $args[0];
  3886. $username = $args[1];
  3887. $password = $args[2];
  3888. if ( isset( $args[3] ) )
  3889. $query = array( 'numberposts' => absint( $args[3] ) );
  3890. else
  3891. $query = array();
  3892. if ( !$user = $this->login($username, $password) )
  3893. return $this->error;
  3894. do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');
  3895. $posts_list = wp_get_recent_posts( $query );
  3896. if ( !$posts_list )
  3897. return array();
  3898. $struct = array();
  3899. foreach ($posts_list as $entry) {
  3900. if ( !current_user_can( 'edit_post', $entry['ID'] ) )
  3901. continue;
  3902. $post_date = $this->_convert_date( $entry['post_date'] );
  3903. $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
  3904. $post_modified = $this->_convert_date( $entry['post_modified'] );
  3905. $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] );
  3906. $categories = array();
  3907. $catids = wp_get_post_categories($entry['ID']);
  3908. foreach( $catids as $catid )
  3909. $categories[] = get_cat_name($catid);
  3910. $tagnames = array();
  3911. $tags = wp_get_post_tags( $entry['ID'] );
  3912. if ( !empty( $tags ) ) {
  3913. foreach ( $tags as $tag ) {
  3914. $tagnames[] = $tag->name;
  3915. }
  3916. $tagnames = implode( ', ', $tagnames );
  3917. } else {
  3918. $tagnames = '';
  3919. }
  3920. $post = get_extended($entry['post_content']);
  3921. $link = post_permalink($entry['ID']);
  3922. // Get the post author info.
  3923. $author = get_userdata($entry['post_author']);
  3924. $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;
  3925. $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;
  3926. // Consider future posts as published
  3927. if ( $entry['post_status'] === 'future' )
  3928. $entry['post_status'] = 'publish';
  3929. // Get post format
  3930. $post_format = get_post_format( $entry['ID'] );
  3931. if ( empty( $post_format ) )
  3932. $post_format = 'standard';
  3933. $struct[] = array(
  3934. 'dateCreated' => $post_date,
  3935. 'userid' => $entry['post_author'],
  3936. 'postid' => (string) $entry['ID'],
  3937. 'description' => $post['main'],
  3938. 'title' => $entry['post_title'],
  3939. 'link' => $link,
  3940. 'permaLink' => $link,
  3941. // commented out because no other tool seems to use this
  3942. // 'content' => $entry['post_content'],
  3943. 'categories' => $categories,
  3944. 'mt_excerpt' => $entry['post_excerpt'],
  3945. 'mt_text_more' => $post['extended'],
  3946. 'wp_more_text' => $post['more_text'],
  3947. 'mt_allow_comments' => $allow_comments,
  3948. 'mt_allow_pings' => $allow_pings,
  3949. 'mt_keywords' => $tagnames,
  3950. 'wp_slug' => $entry['post_name'],
  3951. 'wp_password' => $entry['post_password'],
  3952. 'wp_author_id' => (string) $author->ID,
  3953. 'wp_author_display_name' => $author->display_name,
  3954. 'date_created_gmt' => $post_date_gmt,
  3955. 'post_status' => $entry['post_status'],
  3956. 'custom_fields' => $this->get_custom_fields($entry['ID']),
  3957. 'wp_post_format' => $post_format,
  3958. 'date_modified' => $post_modified,
  3959. 'date_modified_gmt' => $post_modified_gmt
  3960. );
  3961. $entry_index = count( $struct ) - 1;
  3962. $struct[ $entry_index ][ 'wp_post_thumbnail' ] = get_post_thumbnail_id( $entry['ID'] );
  3963. }
  3964. $recent_posts = array();
  3965. for ( $j=0; $j<count($struct); $j++ ) {
  3966. array_push($recent_posts, $struct[$j]);
  3967. }
  3968. return $recent_posts;
  3969. }
  3970. /**
  3971. * Retrieve the list of categories on a given blog.
  3972. *
  3973. * @since 1.5.0
  3974. *
  3975. * @param array $args Method parameters.
  3976. * @return array
  3977. */
  3978. function mw_getCategories($args) {
  3979. $this->escape($args);
  3980. $blog_ID = (int) $args[0];
  3981. $username = $args[1];
  3982. $password = $args[2];
  3983. if ( !$user = $this->login($username, $password) )
  3984. return $this->error;
  3985. if ( !current_user_can( 'edit_posts' ) )
  3986. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
  3987. do_action('xmlrpc_call', 'metaWeblog.getCategories');
  3988. $categories_struct = array();
  3989. if ( $cats = get_categories(array('get' => 'all')) ) {
  3990. foreach ( $cats as $cat ) {
  3991. $struct['categoryId'] = $cat->term_id;
  3992. $struct['parentId'] = $cat->parent;
  3993. $struct['description'] = $cat->name;
  3994. $struct['categoryDescription'] = $cat->description;
  3995. $struct['categoryName'] = $cat->name;
  3996. $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id));
  3997. $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2'));
  3998. $categories_struct[] = $struct;
  3999. }
  4000. }
  4001. return $categories_struct;
  4002. }
  4003. /**
  4004. * Uploads a file, following your settings.
  4005. *
  4006. * Adapted from a patch by Johann Richard.
  4007. *
  4008. * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
  4009. *
  4010. * @since 1.5.0
  4011. *
  4012. * @param array $args Method parameters.
  4013. * @return array
  4014. */
  4015. function mw_newMediaObject($args) {
  4016. global $wpdb;
  4017. $blog_ID = (int) $args[0];
  4018. $username = $wpdb->escape($args[1]);
  4019. $password = $wpdb->escape($args[2]);
  4020. $data = $args[3];
  4021. $name = sanitize_file_name( $data['name'] );
  4022. $type = $data['type'];
  4023. $bits = $data['bits'];
  4024. if ( !$user = $this->login($username, $password) )
  4025. return $this->error;
  4026. do_action('xmlrpc_call', 'metaWeblog.newMediaObject');
  4027. if ( !current_user_can('upload_files') ) {
  4028. $this->error = new IXR_Error( 401, __( 'You do not have permission to upload files.' ) );
  4029. return $this->error;
  4030. }
  4031. if ( $upload_err = apply_filters( 'pre_upload_error', false ) )
  4032. return new IXR_Error(500, $upload_err);
  4033. if ( !empty($data['overwrite']) && ($data['overwrite'] == true) ) {
  4034. // Get postmeta info on the object.
  4035. $old_file = $wpdb->get_row("
  4036. SELECT ID
  4037. FROM {$wpdb->posts}
  4038. WHERE post_title = '{$name}'
  4039. AND post_type = 'attachment'
  4040. ");
  4041. // Delete previous file.
  4042. wp_delete_attachment($old_file->ID);
  4043. // Make sure the new name is different by pre-pending the
  4044. // previous post id.
  4045. $filename = preg_replace('/^wpid\d+-/', '', $name);
  4046. $name = "wpid{$old_file->ID}-{$filename}";
  4047. }
  4048. $upload = wp_upload_bits($name, null, $bits);
  4049. if ( ! empty($upload['error']) ) {
  4050. $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
  4051. return new IXR_Error(500, $errorString);
  4052. }
  4053. // Construct the attachment array
  4054. $post_id = 0;
  4055. if ( ! empty( $data['post_id'] ) ) {
  4056. $post_id = (int) $data['post_id'];
  4057. if ( ! current_user_can( 'edit_post', $post_id ) )
  4058. return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
  4059. }
  4060. $attachment = array(
  4061. 'post_title' => $name,
  4062. 'post_content' => '',
  4063. 'post_type' => 'attachment',
  4064. 'post_parent' => $post_id,
  4065. 'post_mime_type' => $type,
  4066. 'guid' => $upload[ 'url' ]
  4067. );
  4068. // Save the data
  4069. $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
  4070. wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
  4071. do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args );
  4072. $struct = array(
  4073. 'id' => strval( $id ),
  4074. 'file' => $name,
  4075. 'url' => $upload[ 'url' ],
  4076. 'type' => $type
  4077. );
  4078. return apply_filters( 'wp_handle_upload', $struct, 'upload' );
  4079. }
  4080. /* MovableType API functions
  4081. * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html
  4082. */
  4083. /**
  4084. * Retrieve the post titles of recent posts.
  4085. *
  4086. * @since 1.5.0
  4087. *
  4088. * @param array $args Method parameters.
  4089. * @return array
  4090. */
  4091. function mt_getRecentPostTitles($args) {
  4092. $this->escape($args);
  4093. $blog_ID = (int) $args[0];
  4094. $username = $args[1];
  4095. $password = $args[2];
  4096. if ( isset( $args[3] ) )
  4097. $query = array( 'numberposts' => absint( $args[3] ) );
  4098. else
  4099. $query = array();
  4100. if ( !$user = $this->login($username, $password) )
  4101. return $this->error;
  4102. do_action('xmlrpc_call', 'mt.getRecentPostTitles');
  4103. $posts_list = wp_get_recent_posts( $query );
  4104. if ( !$posts_list ) {
  4105. $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
  4106. return $this->error;
  4107. }
  4108. $struct = array();
  4109. foreach ($posts_list as $entry) {
  4110. if ( !current_user_can( 'edit_post', $entry['ID'] ) )
  4111. continue;
  4112. $post_date = $this->_convert_date( $entry['post_date'] );
  4113. $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
  4114. $struct[] = array(
  4115. 'dateCreated' => $post_date,
  4116. 'userid' => $entry['post_author'],
  4117. 'postid' => (string) $entry['ID'],
  4118. 'title' => $entry['post_title'],
  4119. 'post_status' => $entry['post_status'],
  4120. 'date_created_gmt' => $post_date_gmt
  4121. );
  4122. }
  4123. $recent_posts = array();
  4124. for ( $j=0; $j<count($struct); $j++ ) {
  4125. array_push($recent_posts, $struct[$j]);
  4126. }
  4127. return $recent_posts;
  4128. }
  4129. /**
  4130. * Retrieve list of all categories on blog.
  4131. *
  4132. * @since 1.5.0
  4133. *
  4134. * @param array $args Method parameters.
  4135. * @return array
  4136. */
  4137. function mt_getCategoryList($args) {
  4138. $this->escape($args);
  4139. $blog_ID = (int) $args[0];
  4140. $username = $args[1];
  4141. $password = $args[2];
  4142. if ( !$user = $this->login($username, $password) )
  4143. return $this->error;
  4144. if ( !current_user_can( 'edit_posts' ) )
  4145. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
  4146. do_action('xmlrpc_call', 'mt.getCategoryList');
  4147. $categories_struct = array();
  4148. if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) {
  4149. foreach ( $cats as $cat ) {
  4150. $struct['categoryId'] = $cat->term_id;
  4151. $struct['categoryName'] = $cat->name;
  4152. $categories_struct[] = $struct;
  4153. }
  4154. }
  4155. return $categories_struct;
  4156. }
  4157. /**
  4158. * Retrieve post categories.
  4159. *
  4160. * @since 1.5.0
  4161. *
  4162. * @param array $args Method parameters.
  4163. * @return array
  4164. */
  4165. function mt_getPostCategories($args) {
  4166. $this->escape($args);
  4167. $post_ID = (int) $args[0];
  4168. $username = $args[1];
  4169. $password = $args[2];
  4170. if ( !$user = $this->login($username, $password) )
  4171. return $this->error;
  4172. if ( ! get_post( $post_ID ) )
  4173. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  4174. if ( !current_user_can( 'edit_post', $post_ID ) )
  4175. return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
  4176. do_action('xmlrpc_call', 'mt.getPostCategories');
  4177. $categories = array();
  4178. $catids = wp_get_post_categories(intval($post_ID));
  4179. // first listed category will be the primary category
  4180. $isPrimary = true;
  4181. foreach ( $catids as $catid ) {
  4182. $categories[] = array(
  4183. 'categoryName' => get_cat_name($catid),
  4184. 'categoryId' => (string) $catid,
  4185. 'isPrimary' => $isPrimary
  4186. );
  4187. $isPrimary = false;
  4188. }
  4189. return $categories;
  4190. }
  4191. /**
  4192. * Sets categories for a post.
  4193. *
  4194. * @since 1.5.0
  4195. *
  4196. * @param array $args Method parameters.
  4197. * @return bool True on success.
  4198. */
  4199. function mt_setPostCategories($args) {
  4200. $this->escape($args);
  4201. $post_ID = (int) $args[0];
  4202. $username = $args[1];
  4203. $password = $args[2];
  4204. $categories = $args[3];
  4205. if ( !$user = $this->login($username, $password) )
  4206. return $this->error;
  4207. do_action('xmlrpc_call', 'mt.setPostCategories');
  4208. if ( ! get_post( $post_ID ) )
  4209. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  4210. if ( !current_user_can('edit_post', $post_ID) )
  4211. return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
  4212. $catids = array();
  4213. foreach ( $categories as $cat ) {
  4214. $catids[] = $cat['categoryId'];
  4215. }
  4216. wp_set_post_categories($post_ID, $catids);
  4217. return true;
  4218. }
  4219. /**
  4220. * Retrieve an array of methods supported by this server.
  4221. *
  4222. * @since 1.5.0
  4223. *
  4224. * @param array $args Method parameters.
  4225. * @return array
  4226. */
  4227. function mt_supportedMethods($args) {
  4228. do_action('xmlrpc_call', 'mt.supportedMethods');
  4229. $supported_methods = array();
  4230. foreach ( $this->methods as $key => $value ) {
  4231. $supported_methods[] = $key;
  4232. }
  4233. return $supported_methods;
  4234. }
  4235. /**
  4236. * Retrieve an empty array because we don't support per-post text filters.
  4237. *
  4238. * @since 1.5.0
  4239. *
  4240. * @param array $args Method parameters.
  4241. */
  4242. function mt_supportedTextFilters($args) {
  4243. do_action('xmlrpc_call', 'mt.supportedTextFilters');
  4244. return apply_filters('xmlrpc_text_filters', array());
  4245. }
  4246. /**
  4247. * Retrieve trackbacks sent to a given post.
  4248. *
  4249. * @since 1.5.0
  4250. *
  4251. * @param array $args Method parameters.
  4252. * @return mixed
  4253. */
  4254. function mt_getTrackbackPings($args) {
  4255. global $wpdb;
  4256. $post_ID = intval($args);
  4257. do_action('xmlrpc_call', 'mt.getTrackbackPings');
  4258. $actual_post = get_post($post_ID, ARRAY_A);
  4259. if ( !$actual_post )
  4260. return new IXR_Error(404, __('Sorry, no such post.'));
  4261. $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
  4262. if ( !$comments )
  4263. return array();
  4264. $trackback_pings = array();
  4265. foreach ( $comments as $comment ) {
  4266. if ( 'trackback' == $comment->comment_type ) {
  4267. $content = $comment->comment_content;
  4268. $title = substr($content, 8, (strpos($content, '</strong>') - 8));
  4269. $trackback_pings[] = array(
  4270. 'pingTitle' => $title,
  4271. 'pingURL' => $comment->comment_author_url,
  4272. 'pingIP' => $comment->comment_author_IP
  4273. );
  4274. }
  4275. }
  4276. return $trackback_pings;
  4277. }
  4278. /**
  4279. * Sets a post's publish status to 'publish'.
  4280. *
  4281. * @since 1.5.0
  4282. *
  4283. * @param array $args Method parameters.
  4284. * @return int
  4285. */
  4286. function mt_publishPost($args) {
  4287. $this->escape($args);
  4288. $post_ID = (int) $args[0];
  4289. $username = $args[1];
  4290. $password = $args[2];
  4291. if ( !$user = $this->login($username, $password) )
  4292. return $this->error;
  4293. do_action('xmlrpc_call', 'mt.publishPost');
  4294. $postdata = get_post($post_ID, ARRAY_A);
  4295. if ( ! $postdata )
  4296. return new IXR_Error( 404, __( 'Invalid post ID.' ) );
  4297. if ( !current_user_can('publish_posts') || !current_user_can('edit_post', $post_ID) )
  4298. return new IXR_Error(401, __('Sorry, you cannot publish this post.'));
  4299. $postdata['post_status'] = 'publish';
  4300. // retain old cats
  4301. $cats = wp_get_post_categories($post_ID);
  4302. $postdata['post_category'] = $cats;
  4303. $this->escape($postdata);
  4304. $result = wp_update_post($postdata);
  4305. return $result;
  4306. }
  4307. /* PingBack functions
  4308. * specs on www.hixie.ch/specs/pingback/pingback
  4309. */
  4310. /**
  4311. * Retrieves a pingback and registers it.
  4312. *
  4313. * @since 1.5.0
  4314. *
  4315. * @param array $args Method parameters.
  4316. * @return array
  4317. */
  4318. function pingback_ping($args) {
  4319. global $wpdb;
  4320. do_action('xmlrpc_call', 'pingback.ping');
  4321. $this->escape($args);
  4322. $pagelinkedfrom = $args[0];
  4323. $pagelinkedto = $args[1];
  4324. $title = '';
  4325. $pagelinkedfrom = str_replace('&amp;', '&', $pagelinkedfrom);
  4326. $pagelinkedto = str_replace('&amp;', '&', $pagelinkedto);
  4327. $pagelinkedto = str_replace('&', '&amp;', $pagelinkedto);
  4328. $pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto );
  4329. if ( ! $pagelinkedfrom )
  4330. return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) );
  4331. // Check if the page linked to is in our site
  4332. $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
  4333. if ( !$pos1 )
  4334. return $this->pingback_error( 0, __( 'Is there no link to us?' ) );
  4335. // let's find which post is linked to
  4336. // FIXME: does url_to_postid() cover all these cases already?
  4337. // if so, then let's use it and drop the old code.
  4338. $urltest = parse_url($pagelinkedto);
  4339. if ( $post_ID = url_to_postid($pagelinkedto) ) {
  4340. $way = 'url_to_postid()';
  4341. } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {
  4342. // the path defines the post_ID (archives/p/XXXX)
  4343. $blah = explode('/', $match[0]);
  4344. $post_ID = (int) $blah[1];
  4345. $way = 'from the path';
  4346. } elseif ( isset( $urltest['query'] ) && preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
  4347. // the querystring defines the post_ID (?p=XXXX)
  4348. $blah = explode('=', $match[0]);
  4349. $post_ID = (int) $blah[1];
  4350. $way = 'from the querystring';
  4351. } elseif ( isset($urltest['fragment']) ) {
  4352. // an #anchor is there, it's either...
  4353. if ( intval($urltest['fragment']) ) {
  4354. // ...an integer #XXXX (simplest case)
  4355. $post_ID = (int) $urltest['fragment'];
  4356. $way = 'from the fragment (numeric)';
  4357. } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {
  4358. // ...a post id in the form 'post-###'
  4359. $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
  4360. $way = 'from the fragment (post-###)';
  4361. } elseif ( is_string($urltest['fragment']) ) {
  4362. // ...or a string #title, a little more complicated
  4363. $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
  4364. $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", like_escape( $title ) );
  4365. if (! ($post_ID = $wpdb->get_var($sql)) ) {
  4366. // returning unknown error '0' is better than die()ing
  4367. return $this->pingback_error( 0, '' );
  4368. }
  4369. $way = 'from the fragment (title)';
  4370. }
  4371. } else {
  4372. // TODO: Attempt to extract a post ID from the given URL
  4373. return $this->pingback_error( 33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.' ) );
  4374. }
  4375. $post_ID = (int) $post_ID;
  4376. $post = get_post($post_ID);
  4377. if ( !$post ) // Post_ID not found
  4378. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.' ) );
  4379. if ( $post_ID == url_to_postid($pagelinkedfrom) )
  4380. return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) );
  4381. // Check if pings are on
  4382. if ( !pings_open($post) )
  4383. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.' ) );
  4384. // Let's check that the remote site didn't already pingback this entry
  4385. if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )
  4386. return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) );
  4387. // very stupid, but gives time to the 'from' server to publish !
  4388. sleep(1);
  4389. // Let's check the remote site
  4390. $linea = wp_remote_retrieve_body( wp_remote_get( $pagelinkedfrom, array( 'timeout' => 10, 'redirection' => 0, 'reject_unsafe_urls' => true ) ) );
  4391. if ( !$linea )
  4392. return $this->pingback_error( 16, __( 'The source URL does not exist.' ) );
  4393. $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);
  4394. // Work around bug in strip_tags():
  4395. $linea = str_replace('<!DOC', '<DOC', $linea);
  4396. $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
  4397. $linea = preg_replace( "/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
  4398. preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
  4399. $title = $matchtitle[1];
  4400. if ( empty( $title ) )
  4401. return $this->pingback_error( 32, __('We cannot find a title on that page.' ) );
  4402. $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need
  4403. $p = explode( "\n\n", $linea );
  4404. $preg_target = preg_quote($pagelinkedto, '|');
  4405. foreach ( $p as $para ) {
  4406. if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
  4407. preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
  4408. // If the URL isn't in a link context, keep looking
  4409. if ( empty($context) )
  4410. continue;
  4411. // We're going to use this fake tag to mark the context in a bit
  4412. // the marker is needed in case the link text appears more than once in the paragraph
  4413. $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
  4414. // prevent really long link text
  4415. if ( strlen($context[1]) > 100 )
  4416. $context[1] = substr($context[1], 0, 100) . '...';
  4417. $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker
  4418. $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
  4419. $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker
  4420. $excerpt = trim($excerpt);
  4421. $preg_marker = preg_quote($marker, '|');
  4422. $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
  4423. $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
  4424. break;
  4425. }
  4426. }
  4427. if ( empty($context) ) // Link to target not found
  4428. return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
  4429. $pagelinkedfrom = str_replace('&', '&amp;', $pagelinkedfrom);
  4430. $context = '[...] ' . esc_html( $excerpt ) . ' [...]';
  4431. $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );
  4432. $comment_post_ID = (int) $post_ID;
  4433. $comment_author = $title;
  4434. $comment_author_email = '';
  4435. $this->escape($comment_author);
  4436. $comment_author_url = $pagelinkedfrom;
  4437. $comment_content = $context;
  4438. $this->escape($comment_content);
  4439. $comment_type = 'pingback';
  4440. $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_content', 'comment_type');
  4441. $comment_ID = wp_new_comment($commentdata);
  4442. do_action('pingback_post', $comment_ID);
  4443. return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);
  4444. }
  4445. /**
  4446. * Retrieve array of URLs that pingbacked the given URL.
  4447. *
  4448. * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
  4449. *
  4450. * @since 1.5.0
  4451. *
  4452. * @param array $args Method parameters.
  4453. * @return array
  4454. */
  4455. function pingback_extensions_getPingbacks($args) {
  4456. global $wpdb;
  4457. do_action('xmlrpc_call', 'pingback.extensions.getPingbacks');
  4458. $this->escape($args);
  4459. $url = $args;
  4460. $post_ID = url_to_postid($url);
  4461. if ( !$post_ID ) {
  4462. // We aren't sure that the resource is available and/or pingback enabled
  4463. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.' ) );
  4464. }
  4465. $actual_post = get_post($post_ID, ARRAY_A);
  4466. if ( !$actual_post ) {
  4467. // No such post = resource not found
  4468. return $this->pingback_error( 32, __('The specified target URL does not exist.' ) );
  4469. }
  4470. $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
  4471. if ( !$comments )
  4472. return array();
  4473. $pingbacks = array();
  4474. foreach ( $comments as $comment ) {
  4475. if ( 'pingback' == $comment->comment_type )
  4476. $pingbacks[] = $comment->comment_author_url;
  4477. }
  4478. return $pingbacks;
  4479. }
  4480. protected function pingback_error( $code, $message ) {
  4481. return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) );
  4482. }
  4483. }