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

  1. <?php 
  2. /** 
  3. * XML-RPC protocol support for WordPress 
  4. * 
  5. * @package WordPress 
  6. * @subpackage Publishing 
  7. */ 
  8.  
  9. /** 
  10. * WordPress XMLRPC server implementation. 
  11. * 
  12. * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and 
  13. * pingback. Additional WordPress API for managing comments, pages, posts,  
  14. * options, etc. 
  15. * 
  16. * As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled 
  17. * via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::login(). 
  18. * 
  19. * @package WordPress 
  20. * @subpackage Publishing 
  21. * @since 1.5.0 
  22. */ 
  23. class wp_xmlrpc_server extends IXR_Server { 
  24. /** 
  25. * Methods. 
  26. * 
  27. * @access public 
  28. * @var array 
  29. */ 
  30. public $methods; 
  31.  
  32. /** 
  33. * Blog options. 
  34. * 
  35. * @access public 
  36. * @var array 
  37. */ 
  38. public $blog_options; 
  39.  
  40. /** 
  41. * IXR_Error instance. 
  42. * 
  43. * @access public 
  44. * @var IXR_Error 
  45. */ 
  46. public $error; 
  47.  
  48. /** 
  49. * Flags that the user authentication has failed in this instance of wp_xmlrpc_server. 
  50. * 
  51. * @access protected 
  52. * @var bool 
  53. */ 
  54. protected $auth_failed = false; 
  55.  
  56. /** 
  57. * Registers all of the XMLRPC methods that XMLRPC server understands. 
  58. * 
  59. * Sets up server and method property. Passes XMLRPC 
  60. * methods through the {@see 'xmlrpc_methods'} filter to allow plugins to extend 
  61. * or replace XML-RPC methods. 
  62. * 
  63. * @since 1.5.0 
  64. */ 
  65. public function __construct() { 
  66. $this->methods = array( 
  67. // WordPress API 
  68. 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',  
  69. 'wp.newPost' => 'this:wp_newPost',  
  70. 'wp.editPost' => 'this:wp_editPost',  
  71. 'wp.deletePost' => 'this:wp_deletePost',  
  72. 'wp.getPost' => 'this:wp_getPost',  
  73. 'wp.getPosts' => 'this:wp_getPosts',  
  74. 'wp.newTerm' => 'this:wp_newTerm',  
  75. 'wp.editTerm' => 'this:wp_editTerm',  
  76. 'wp.deleteTerm' => 'this:wp_deleteTerm',  
  77. 'wp.getTerm' => 'this:wp_getTerm',  
  78. 'wp.getTerms' => 'this:wp_getTerms',  
  79. 'wp.getTaxonomy' => 'this:wp_getTaxonomy',  
  80. 'wp.getTaxonomies' => 'this:wp_getTaxonomies',  
  81. 'wp.getUser' => 'this:wp_getUser',  
  82. 'wp.getUsers' => 'this:wp_getUsers',  
  83. 'wp.getProfile' => 'this:wp_getProfile',  
  84. 'wp.editProfile' => 'this:wp_editProfile',  
  85. 'wp.getPage' => 'this:wp_getPage',  
  86. 'wp.getPages' => 'this:wp_getPages',  
  87. 'wp.newPage' => 'this:wp_newPage',  
  88. 'wp.deletePage' => 'this:wp_deletePage',  
  89. 'wp.editPage' => 'this:wp_editPage',  
  90. 'wp.getPageList' => 'this:wp_getPageList',  
  91. 'wp.getAuthors' => 'this:wp_getAuthors',  
  92. 'wp.getCategories' => 'this:mw_getCategories', // Alias 
  93. 'wp.getTags' => 'this:wp_getTags',  
  94. 'wp.newCategory' => 'this:wp_newCategory',  
  95. 'wp.deleteCategory' => 'this:wp_deleteCategory',  
  96. 'wp.suggestCategories' => 'this:wp_suggestCategories',  
  97. 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias 
  98. 'wp.deleteFile' => 'this:wp_deletePost', // Alias 
  99. 'wp.getCommentCount' => 'this:wp_getCommentCount',  
  100. 'wp.getPostStatusList' => 'this:wp_getPostStatusList',  
  101. 'wp.getPageStatusList' => 'this:wp_getPageStatusList',  
  102. 'wp.getPageTemplates' => 'this:wp_getPageTemplates',  
  103. 'wp.getOptions' => 'this:wp_getOptions',  
  104. 'wp.setOptions' => 'this:wp_setOptions',  
  105. 'wp.getComment' => 'this:wp_getComment',  
  106. 'wp.getComments' => 'this:wp_getComments',  
  107. 'wp.deleteComment' => 'this:wp_deleteComment',  
  108. 'wp.editComment' => 'this:wp_editComment',  
  109. 'wp.newComment' => 'this:wp_newComment',  
  110. 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',  
  111. 'wp.getMediaItem' => 'this:wp_getMediaItem',  
  112. 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary',  
  113. 'wp.getPostFormats' => 'this:wp_getPostFormats',  
  114. 'wp.getPostType' => 'this:wp_getPostType',  
  115. 'wp.getPostTypes' => 'this:wp_getPostTypes',  
  116. 'wp.getRevisions' => 'this:wp_getRevisions',  
  117. 'wp.restoreRevision' => 'this:wp_restoreRevision',  
  118.  
  119. // Blogger API 
  120. 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',  
  121. 'blogger.getUserInfo' => 'this:blogger_getUserInfo',  
  122. 'blogger.getPost' => 'this:blogger_getPost',  
  123. 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',  
  124. 'blogger.newPost' => 'this:blogger_newPost',  
  125. 'blogger.editPost' => 'this:blogger_editPost',  
  126. 'blogger.deletePost' => 'this:blogger_deletePost',  
  127.  
  128. // MetaWeblog API (with MT extensions to structs) 
  129. 'metaWeblog.newPost' => 'this:mw_newPost',  
  130. 'metaWeblog.editPost' => 'this:mw_editPost',  
  131. 'metaWeblog.getPost' => 'this:mw_getPost',  
  132. 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',  
  133. 'metaWeblog.getCategories' => 'this:mw_getCategories',  
  134. 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',  
  135.  
  136. // MetaWeblog API aliases for Blogger API 
  137. // see http://www.xmlrpc.com/stories/storyReader$2460 
  138. 'metaWeblog.deletePost' => 'this:blogger_deletePost',  
  139. 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',  
  140.  
  141. // MovableType API 
  142. 'mt.getCategoryList' => 'this:mt_getCategoryList',  
  143. 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',  
  144. 'mt.getPostCategories' => 'this:mt_getPostCategories',  
  145. 'mt.setPostCategories' => 'this:mt_setPostCategories',  
  146. 'mt.supportedMethods' => 'this:mt_supportedMethods',  
  147. 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',  
  148. 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',  
  149. 'mt.publishPost' => 'this:mt_publishPost',  
  150.  
  151. // PingBack 
  152. 'pingback.ping' => 'this:pingback_ping',  
  153. 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',  
  154.  
  155. 'demo.sayHello' => 'this:sayHello',  
  156. 'demo.addTwoNumbers' => 'this:addTwoNumbers' 
  157. ); 
  158.  
  159. $this->initialise_blog_option_info(); 
  160.  
  161. /** 
  162. * Filters the methods exposed by the XML-RPC server. 
  163. * 
  164. * This filter can be used to add new methods, and remove built-in methods. 
  165. * 
  166. * @since 1.5.0 
  167. * 
  168. * @param array $methods An array of XML-RPC methods. 
  169. */ 
  170. $this->methods = apply_filters( 'xmlrpc_methods', $this->methods ); 
  171.  
  172. /** 
  173. * Make private/protected methods readable for backward compatibility. 
  174. * 
  175. * @since 4.0.0 
  176. * @access public 
  177. * 
  178. * @param callable $name Method to call. 
  179. * @param array $arguments Arguments to pass when calling. 
  180. * @return array|IXR_Error|false Return value of the callback, false otherwise. 
  181. */ 
  182. public function __call( $name, $arguments ) { 
  183. if ( '_multisite_getUsersBlogs' === $name ) { 
  184. return call_user_func_array( array( $this, $name ), $arguments ); 
  185. return false; 
  186.  
  187. /** 
  188. * Serves the XML-RPC request. 
  189. * 
  190. * @since 2.9.0 
  191. * @access public 
  192. */ 
  193. public function serve_request() { 
  194. $this->IXR_Server($this->methods); 
  195.  
  196. /** 
  197. * Test XMLRPC API by saying, "Hello!" to client. 
  198. * 
  199. * @since 1.5.0 
  200. * 
  201. * @return string Hello string response. 
  202. */ 
  203. public function sayHello() { 
  204. return 'Hello!'; 
  205.  
  206. /** 
  207. * Test XMLRPC API by adding two numbers for client. 
  208. * 
  209. * @since 1.5.0 
  210. * 
  211. * @param array $args { 
  212. * Method arguments. Note: arguments must be ordered as documented. 
  213. * 
  214. * @type int $number1 A number to add. 
  215. * @type int $number2 A second number to add. 
  216. * } 
  217. * @return int Sum of the two given numbers. 
  218. */ 
  219. public function addTwoNumbers( $args ) { 
  220. $number1 = $args[0]; 
  221. $number2 = $args[1]; 
  222. return $number1 + $number2; 
  223.  
  224. /** 
  225. * Log user in. 
  226. * 
  227. * @since 2.8.0 
  228. * 
  229. * @param string $username User's username. 
  230. * @param string $password User's password. 
  231. * @return WP_User|bool WP_User object if authentication passed, false otherwise 
  232. */ 
  233. public function login( $username, $password ) { 
  234. /** 
  235. * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc' 
  236. * option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead. 
  237. */ 
  238. $enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); 
  239. if ( false === $enabled ) { 
  240. $enabled = apply_filters( 'option_enable_xmlrpc', true ); 
  241.  
  242. /** 
  243. * Filters whether XML-RPC methods requiring authentication are enabled. 
  244. * 
  245. * Contrary to the way it's named, this filter does not control whether XML-RPC is *fully* 
  246. * enabled, rather, it only controls whether XML-RPC methods requiring authentication - such 
  247. * as for publishing purposes - are enabled. 
  248. * 
  249. * Further, the filter does not control whether pingbacks or other custom endpoints that don't 
  250. * require authentication are enabled. This behavior is expected, and due to how parity was matched 
  251. * with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5. 
  252. * 
  253. * To disable XML-RPC methods that require authentication, use: 
  254. * 
  255. * add_filter( 'xmlrpc_enabled', '__return_false' ); 
  256. * 
  257. * For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'} 
  258. * and {@see 'xmlrpc_element_limit'} hooks. 
  259. * 
  260. * @since 3.5.0 
  261. * 
  262. * @param bool $enabled Whether XML-RPC is enabled. Default true. 
  263. */ 
  264. $enabled = apply_filters( 'xmlrpc_enabled', $enabled ); 
  265.  
  266. if ( ! $enabled ) { 
  267. $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) ); 
  268. return false; 
  269.  
  270. if ( $this->auth_failed ) { 
  271. $user = new WP_Error( 'login_prevented' ); 
  272. } else { 
  273. $user = wp_authenticate( $username, $password ); 
  274.  
  275. if ( is_wp_error( $user ) ) { 
  276. $this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) ); 
  277.  
  278. // Flag that authentication has failed once on this wp_xmlrpc_server instance 
  279. $this->auth_failed = true; 
  280.  
  281. /** 
  282. * Filters the XML-RPC user login error message. 
  283. * 
  284. * @since 3.5.0 
  285. * 
  286. * @param string $error The XML-RPC error message. 
  287. * @param WP_User $user WP_User object. 
  288. */ 
  289. $this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user ); 
  290. return false; 
  291.  
  292. wp_set_current_user( $user->ID ); 
  293. return $user; 
  294.  
  295. /** 
  296. * Check user's credentials. Deprecated. 
  297. * 
  298. * @since 1.5.0 
  299. * @deprecated 2.8.0 Use wp_xmlrpc_server::login() 
  300. * @see wp_xmlrpc_server::login() 
  301. * 
  302. * @param string $username User's username. 
  303. * @param string $password User's password. 
  304. * @return bool Whether authentication passed. 
  305. */ 
  306. public function login_pass_ok( $username, $password ) { 
  307. return (bool) $this->login( $username, $password ); 
  308.  
  309. /** 
  310. * Escape string or array of strings for database. 
  311. * 
  312. * @since 1.5.2 
  313. * 
  314. * @param string|array $data Escape single string or array of strings. 
  315. * @return string|void Returns with string is passed, alters by-reference 
  316. * when array is passed. 
  317. */ 
  318. public function escape( &$data ) { 
  319. if ( ! is_array( $data ) ) 
  320. return wp_slash( $data ); 
  321.  
  322. foreach ( $data as &$v ) { 
  323. if ( is_array( $v ) ) 
  324. $this->escape( $v ); 
  325. elseif ( ! is_object( $v ) ) 
  326. $v = wp_slash( $v ); 
  327.  
  328. /** 
  329. * Retrieve custom fields for post. 
  330. * 
  331. * @since 2.5.0 
  332. * 
  333. * @param int $post_id Post ID. 
  334. * @return array Custom fields, if exist. 
  335. */ 
  336. public function get_custom_fields($post_id) { 
  337. $post_id = (int) $post_id; 
  338.  
  339. $custom_fields = array(); 
  340.  
  341. foreach ( (array) has_meta($post_id) as $meta ) { 
  342. // Don't expose protected fields. 
  343. if ( ! current_user_can( 'edit_post_meta', $post_id , $meta['meta_key'] ) ) 
  344. continue; 
  345.  
  346. $custom_fields[] = array( 
  347. "id" => $meta['meta_id'],  
  348. "key" => $meta['meta_key'],  
  349. "value" => $meta['meta_value'] 
  350. ); 
  351.  
  352. return $custom_fields; 
  353.  
  354. /** 
  355. * Set custom fields for post. 
  356. * 
  357. * @since 2.5.0 
  358. * 
  359. * @param int $post_id Post ID. 
  360. * @param array $fields Custom fields. 
  361. */ 
  362. public function set_custom_fields($post_id, $fields) { 
  363. $post_id = (int) $post_id; 
  364.  
  365. foreach ( (array) $fields as $meta ) { 
  366. if ( isset($meta['id']) ) { 
  367. $meta['id'] = (int) $meta['id']; 
  368. $pmeta = get_metadata_by_mid( 'post', $meta['id'] ); 
  369. if ( isset($meta['key']) ) { 
  370. $meta['key'] = wp_unslash( $meta['key'] ); 
  371. if ( $meta['key'] !== $pmeta->meta_key ) 
  372. continue; 
  373. $meta['value'] = wp_unslash( $meta['value'] ); 
  374. if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) ) 
  375. update_metadata_by_mid( 'post', $meta['id'], $meta['value'] ); 
  376. } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) { 
  377. delete_metadata_by_mid( 'post', $meta['id'] ); 
  378. } elseif ( current_user_can( 'add_post_meta', $post_id, wp_unslash( $meta['key'] ) ) ) { 
  379. add_post_meta( $post_id, $meta['key'], $meta['value'] ); 
  380.  
  381. /** 
  382. * Set up blog options property. 
  383. * 
  384. * Passes property through {@see 'xmlrpc_blog_options'} filter. 
  385. * 
  386. * @since 2.6.0 
  387. */ 
  388. public function initialise_blog_option_info() { 
  389. $this->blog_options = array( 
  390. // Read only options 
  391. 'software_name' => array( 
  392. 'desc' => __( 'Software Name' ),  
  393. 'readonly' => true,  
  394. 'value' => 'WordPress' 
  395. ),  
  396. 'software_version' => array( 
  397. 'desc' => __( 'Software Version' ),  
  398. 'readonly' => true,  
  399. 'value' => get_bloginfo( 'version' ) 
  400. ),  
  401. 'blog_url' => array( 
  402. 'desc' => __( 'WordPress Address (URL)' ),  
  403. 'readonly' => true,  
  404. 'option' => 'siteurl' 
  405. ),  
  406. 'home_url' => array( 
  407. 'desc' => __( 'Site Address (URL)' ),  
  408. 'readonly' => true,  
  409. 'option' => 'home' 
  410. ),  
  411. 'login_url' => array( 
  412. 'desc' => __( 'Login Address (URL)' ),  
  413. 'readonly' => true,  
  414. 'value' => wp_login_url( ) 
  415. ),  
  416. 'admin_url' => array( 
  417. 'desc' => __( 'The URL to the admin area' ),  
  418. 'readonly' => true,  
  419. 'value' => get_admin_url( ) 
  420. ),  
  421. 'image_default_link_type' => array( 
  422. 'desc' => __( 'Image default link type' ),  
  423. 'readonly' => true,  
  424. 'option' => 'image_default_link_type' 
  425. ),  
  426. 'image_default_size' => array( 
  427. 'desc' => __( 'Image default size' ),  
  428. 'readonly' => true,  
  429. 'option' => 'image_default_size' 
  430. ),  
  431. 'image_default_align' => array( 
  432. 'desc' => __( 'Image default align' ),  
  433. 'readonly' => true,  
  434. 'option' => 'image_default_align' 
  435. ),  
  436. 'template' => array( 
  437. 'desc' => __( 'Template' ),  
  438. 'readonly' => true,  
  439. 'option' => 'template' 
  440. ),  
  441. 'stylesheet' => array( 
  442. 'desc' => __( 'Stylesheet' ),  
  443. 'readonly' => true,  
  444. 'option' => 'stylesheet' 
  445. ),  
  446. 'post_thumbnail' => array( 
  447. 'desc' => __('Post Thumbnail'),  
  448. 'readonly' => true,  
  449. 'value' => current_theme_supports( 'post-thumbnails' ) 
  450. ),  
  451.  
  452. // Updatable options 
  453. 'time_zone' => array( 
  454. 'desc' => __( 'Time Zone' ),  
  455. 'readonly' => false,  
  456. 'option' => 'gmt_offset' 
  457. ),  
  458. 'blog_title' => array( 
  459. 'desc' => __( 'Site Title' ),  
  460. 'readonly' => false,  
  461. 'option' => 'blogname' 
  462. ),  
  463. 'blog_tagline' => array( 
  464. 'desc' => __( 'Site Tagline' ),  
  465. 'readonly' => false,  
  466. 'option' => 'blogdescription' 
  467. ),  
  468. 'date_format' => array( 
  469. 'desc' => __( 'Date Format' ),  
  470. 'readonly' => false,  
  471. 'option' => 'date_format' 
  472. ),  
  473. 'time_format' => array( 
  474. 'desc' => __( 'Time Format' ),  
  475. 'readonly' => false,  
  476. 'option' => 'time_format' 
  477. ),  
  478. 'users_can_register' => array( 
  479. 'desc' => __( 'Allow new users to sign up' ),  
  480. 'readonly' => false,  
  481. 'option' => 'users_can_register' 
  482. ),  
  483. 'thumbnail_size_w' => array( 
  484. 'desc' => __( 'Thumbnail Width' ),  
  485. 'readonly' => false,  
  486. 'option' => 'thumbnail_size_w' 
  487. ),  
  488. 'thumbnail_size_h' => array( 
  489. 'desc' => __( 'Thumbnail Height' ),  
  490. 'readonly' => false,  
  491. 'option' => 'thumbnail_size_h' 
  492. ),  
  493. 'thumbnail_crop' => array( 
  494. 'desc' => __( 'Crop thumbnail to exact dimensions' ),  
  495. 'readonly' => false,  
  496. 'option' => 'thumbnail_crop' 
  497. ),  
  498. 'medium_size_w' => array( 
  499. 'desc' => __( 'Medium size image width' ),  
  500. 'readonly' => false,  
  501. 'option' => 'medium_size_w' 
  502. ),  
  503. 'medium_size_h' => array( 
  504. 'desc' => __( 'Medium size image height' ),  
  505. 'readonly' => false,  
  506. 'option' => 'medium_size_h' 
  507. ),  
  508. 'medium_large_size_w' => array( 
  509. 'desc' => __( 'Medium-Large size image width' ),  
  510. 'readonly' => false,  
  511. 'option' => 'medium_large_size_w' 
  512. ),  
  513. 'medium_large_size_h' => array( 
  514. 'desc' => __( 'Medium-Large size image height' ),  
  515. 'readonly' => false,  
  516. 'option' => 'medium_large_size_h' 
  517. ),  
  518. 'large_size_w' => array( 
  519. 'desc' => __( 'Large size image width' ),  
  520. 'readonly' => false,  
  521. 'option' => 'large_size_w' 
  522. ),  
  523. 'large_size_h' => array( 
  524. 'desc' => __( 'Large size image height' ),  
  525. 'readonly' => false,  
  526. 'option' => 'large_size_h' 
  527. ),  
  528. 'default_comment_status' => array( 
  529. 'desc' => __( 'Allow people to post comments on new articles' ),  
  530. 'readonly' => false,  
  531. 'option' => 'default_comment_status' 
  532. ),  
  533. 'default_ping_status' => array( 
  534. 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new articles' ),  
  535. 'readonly' => false,  
  536. 'option' => 'default_ping_status' 
  537. ); 
  538.  
  539. /** 
  540. * Filters the XML-RPC blog options property. 
  541. * 
  542. * @since 2.6.0 
  543. * 
  544. * @param array $blog_options An array of XML-RPC blog options. 
  545. */ 
  546. $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options ); 
  547.  
  548. /** 
  549. * Retrieve the blogs of the user. 
  550. * 
  551. * @since 2.6.0 
  552. * 
  553. * @param array $args { 
  554. * Method arguments. Note: arguments must be ordered as documented. 
  555. * 
  556. * @type string $username Username. 
  557. * @type string $password Password. 
  558. * } 
  559. * @return array|IXR_Error Array contains: 
  560. * - 'isAdmin' 
  561. * - 'isPrimary' - whether the blog is the user's primary blog 
  562. * - 'url' 
  563. * - 'blogid' 
  564. * - 'blogName' 
  565. * - 'xmlrpc' - url of xmlrpc endpoint 
  566. */ 
  567. public function wp_getUsersBlogs( $args ) { 
  568. if ( ! $this->minimum_args( $args, 2 ) ) { 
  569. return $this->error; 
  570.  
  571. // If this isn't on WPMU then just use blogger_getUsersBlogs 
  572. if ( !is_multisite() ) { 
  573. array_unshift( $args, 1 ); 
  574. return $this->blogger_getUsersBlogs( $args ); 
  575.  
  576. $this->escape( $args ); 
  577.  
  578. $username = $args[0]; 
  579. $password = $args[1]; 
  580.  
  581. if ( !$user = $this->login($username, $password) ) 
  582. return $this->error; 
  583.  
  584. /** 
  585. * Fires after the XML-RPC user has been authenticated but before the rest of 
  586. * the method logic begins. 
  587. * 
  588. * All built-in XML-RPC methods use the action xmlrpc_call, with a parameter 
  589. * equal to the method's name, e.g., wp.getUsersBlogs, wp.newPost, etc. 
  590. * 
  591. * @since 2.5.0 
  592. * 
  593. * @param string $name The method name. 
  594. */ 
  595. do_action( 'xmlrpc_call', 'wp.getUsersBlogs' ); 
  596.  
  597. $blogs = (array) get_blogs_of_user( $user->ID ); 
  598. $struct = array(); 
  599. $primary_blog_id = 0; 
  600. $active_blog = get_active_blog_for_user( $user->ID ); 
  601. if ( $active_blog ) { 
  602. $primary_blog_id = (int) $active_blog->blog_id; 
  603.  
  604. foreach ( $blogs as $blog ) { 
  605. // Don't include blogs that aren't hosted at this site. 
  606. if ( $blog->site_id != get_current_network_id() ) 
  607. continue; 
  608.  
  609. $blog_id = $blog->userblog_id; 
  610.  
  611. switch_to_blog( $blog_id ); 
  612.  
  613. $is_admin = current_user_can( 'manage_options' ); 
  614. $is_primary = ( (int) $blog_id === $primary_blog_id ); 
  615.  
  616. $struct[] = array( 
  617. 'isAdmin' => $is_admin,  
  618. 'isPrimary' => $is_primary,  
  619. 'url' => home_url( '/' ),  
  620. 'blogid' => (string) $blog_id,  
  621. 'blogName' => get_option( 'blogname' ),  
  622. 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),  
  623. ); 
  624.  
  625. restore_current_blog(); 
  626.  
  627. return $struct; 
  628.  
  629. /** 
  630. * Checks if the method received at least the minimum number of arguments. 
  631. * 
  632. * @since 3.4.0 
  633. * @access protected 
  634. * 
  635. * @param string|array $args Sanitize single string or array of strings. 
  636. * @param int $count Minimum number of arguments. 
  637. * @return bool if `$args` contains at least $count arguments. 
  638. */ 
  639. protected function minimum_args( $args, $count ) { 
  640. if ( count( $args ) < $count ) { 
  641. $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) ); 
  642. return false; 
  643.  
  644. return true; 
  645.  
  646. /** 
  647. * Prepares taxonomy data for return in an XML-RPC object. 
  648. * 
  649. * @access protected 
  650. * 
  651. * @param object $taxonomy The unprepared taxonomy data. 
  652. * @param array $fields The subset of taxonomy fields to return. 
  653. * @return array The prepared taxonomy data. 
  654. */ 
  655. protected function _prepare_taxonomy( $taxonomy, $fields ) { 
  656. $_taxonomy = array( 
  657. 'name' => $taxonomy->name,  
  658. 'label' => $taxonomy->label,  
  659. 'hierarchical' => (bool) $taxonomy->hierarchical,  
  660. 'public' => (bool) $taxonomy->public,  
  661. 'show_ui' => (bool) $taxonomy->show_ui,  
  662. '_builtin' => (bool) $taxonomy->_builtin,  
  663. ); 
  664.  
  665. if ( in_array( 'labels', $fields ) ) 
  666. $_taxonomy['labels'] = (array) $taxonomy->labels; 
  667.  
  668. if ( in_array( 'cap', $fields ) ) 
  669. $_taxonomy['cap'] = (array) $taxonomy->cap; 
  670.  
  671. if ( in_array( 'menu', $fields ) ) 
  672. $_taxonomy['show_in_menu'] = (bool) $_taxonomy->show_in_menu; 
  673.  
  674. if ( in_array( 'object_type', $fields ) ) 
  675. $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type ); 
  676.  
  677. /** 
  678. * Filters XML-RPC-prepared data for the given taxonomy. 
  679. * 
  680. * @since 3.4.0 
  681. * 
  682. * @param array $_taxonomy An array of taxonomy data. 
  683. * @param WP_Taxonomy $taxonomy Taxonomy object. 
  684. * @param array $fields The subset of taxonomy fields to return. 
  685. */ 
  686. return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields ); 
  687.  
  688. /** 
  689. * Prepares term data for return in an XML-RPC object. 
  690. * 
  691. * @access protected 
  692. * 
  693. * @param array|object $term The unprepared term data. 
  694. * @return array The prepared term data. 
  695. */ 
  696. protected function _prepare_term( $term ) { 
  697. $_term = $term; 
  698. if ( ! is_array( $_term ) ) 
  699. $_term = get_object_vars( $_term ); 
  700.  
  701. // For integers which may be larger than XML-RPC supports ensure we return strings. 
  702. $_term['term_id'] = strval( $_term['term_id'] ); 
  703. $_term['term_group'] = strval( $_term['term_group'] ); 
  704. $_term['term_taxonomy_id'] = strval( $_term['term_taxonomy_id'] ); 
  705. $_term['parent'] = strval( $_term['parent'] ); 
  706.  
  707. // Count we are happy to return as an integer because people really shouldn't use terms that much. 
  708. $_term['count'] = intval( $_term['count'] ); 
  709.  
  710. /** 
  711. * Filters XML-RPC-prepared data for the given term. 
  712. * 
  713. * @since 3.4.0 
  714. * 
  715. * @param array $_term An array of term data. 
  716. * @param array|object $term Term object or array. 
  717. */ 
  718. return apply_filters( 'xmlrpc_prepare_term', $_term, $term ); 
  719.  
  720. /** 
  721. * Convert a WordPress date string to an IXR_Date object. 
  722. * 
  723. * @access protected 
  724. * 
  725. * @param string $date Date string to convert. 
  726. * @return IXR_Date IXR_Date object. 
  727. */ 
  728. protected function _convert_date( $date ) { 
  729. if ( $date === '0000-00-00 00:00:00' ) { 
  730. return new IXR_Date( '00000000T00:00:00Z' ); 
  731. return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) ); 
  732.  
  733. /** 
  734. * Convert a WordPress GMT date string to an IXR_Date object. 
  735. * 
  736. * @access protected 
  737. * 
  738. * @param string $date_gmt WordPress GMT date string. 
  739. * @param string $date Date string. 
  740. * @return IXR_Date IXR_Date object. 
  741. */ 
  742. protected function _convert_date_gmt( $date_gmt, $date ) { 
  743. if ( $date !== '0000-00-00 00:00:00' && $date_gmt === '0000-00-00 00:00:00' ) { 
  744. return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) ); 
  745. return $this->_convert_date( $date_gmt ); 
  746.  
  747. /** 
  748. * Prepares post data for return in an XML-RPC object. 
  749. * 
  750. * @access protected 
  751. * 
  752. * @param array $post The unprepared post data. 
  753. * @param array $fields The subset of post type fields to return. 
  754. * @return array The prepared post data. 
  755. */ 
  756. protected function _prepare_post( $post, $fields ) { 
  757. // Holds the data for this post. built up based on $fields. 
  758. $_post = array( 'post_id' => strval( $post['ID'] ) ); 
  759.  
  760. // Prepare common post fields. 
  761. $post_fields = array( 
  762. 'post_title' => $post['post_title'],  
  763. 'post_date' => $this->_convert_date( $post['post_date'] ),  
  764. 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ),  
  765. 'post_modified' => $this->_convert_date( $post['post_modified'] ),  
  766. 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ),  
  767. 'post_status' => $post['post_status'],  
  768. 'post_type' => $post['post_type'],  
  769. 'post_name' => $post['post_name'],  
  770. 'post_author' => $post['post_author'],  
  771. 'post_password' => $post['post_password'],  
  772. 'post_excerpt' => $post['post_excerpt'],  
  773. 'post_content' => $post['post_content'],  
  774. 'post_parent' => strval( $post['post_parent'] ),  
  775. 'post_mime_type' => $post['post_mime_type'],  
  776. 'link' => get_permalink( $post['ID'] ),  
  777. 'guid' => $post['guid'],  
  778. 'menu_order' => intval( $post['menu_order'] ),  
  779. 'comment_status' => $post['comment_status'],  
  780. 'ping_status' => $post['ping_status'],  
  781. 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),  
  782. ); 
  783.  
  784. // Thumbnail. 
  785. $post_fields['post_thumbnail'] = array(); 
  786. $thumbnail_id = get_post_thumbnail_id( $post['ID'] ); 
  787. if ( $thumbnail_id ) { 
  788. $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail'; 
  789. $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size ); 
  790.  
  791. // Consider future posts as published. 
  792. if ( $post_fields['post_status'] === 'future' ) 
  793. $post_fields['post_status'] = 'publish'; 
  794.  
  795. // Fill in blank post format. 
  796. $post_fields['post_format'] = get_post_format( $post['ID'] ); 
  797. if ( empty( $post_fields['post_format'] ) ) 
  798. $post_fields['post_format'] = 'standard'; 
  799.  
  800. // Merge requested $post_fields fields into $_post. 
  801. if ( in_array( 'post', $fields ) ) { 
  802. $_post = array_merge( $_post, $post_fields ); 
  803. } else { 
  804. $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) ); 
  805. $_post = array_merge( $_post, $requested_fields ); 
  806.  
  807. $all_taxonomy_fields = in_array( 'taxonomies', $fields ); 
  808.  
  809. if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) { 
  810. $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' ); 
  811. $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies ); 
  812. $_post['terms'] = array(); 
  813. foreach ( $terms as $term ) { 
  814. $_post['terms'][] = $this->_prepare_term( $term ); 
  815.  
  816. if ( in_array( 'custom_fields', $fields ) ) 
  817. $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] ); 
  818.  
  819. if ( in_array( 'enclosure', $fields ) ) { 
  820. $_post['enclosure'] = array(); 
  821. $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' ); 
  822. if ( ! empty( $enclosures ) ) { 
  823. $encdata = explode( "\n", $enclosures[0] ); 
  824. $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) ); 
  825. $_post['enclosure']['length'] = (int) trim( $encdata[1] ); 
  826. $_post['enclosure']['type'] = trim( $encdata[2] ); 
  827.  
  828. /** 
  829. * Filters XML-RPC-prepared date for the given post. 
  830. * 
  831. * @since 3.4.0 
  832. * 
  833. * @param array $_post An array of modified post data. 
  834. * @param array $post An array of post data. 
  835. * @param array $fields An array of post fields. 
  836. */ 
  837. return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields ); 
  838.  
  839. /** 
  840. * Prepares post data for return in an XML-RPC object. 
  841. * 
  842. * @since 3.4.0 
  843. * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. 
  844. * @access protected 
  845. * 
  846. * @param WP_Post_Type $post_type Post type object. 
  847. * @param array $fields The subset of post fields to return. 
  848. * @return array The prepared post type data. 
  849. */ 
  850. protected function _prepare_post_type( $post_type, $fields ) { 
  851. $_post_type = array( 
  852. 'name' => $post_type->name,  
  853. 'label' => $post_type->label,  
  854. 'hierarchical' => (bool) $post_type->hierarchical,  
  855. 'public' => (bool) $post_type->public,  
  856. 'show_ui' => (bool) $post_type->show_ui,  
  857. '_builtin' => (bool) $post_type->_builtin,  
  858. 'has_archive' => (bool) $post_type->has_archive,  
  859. 'supports' => get_all_post_type_supports( $post_type->name ),  
  860. ); 
  861.  
  862. if ( in_array( 'labels', $fields ) ) { 
  863. $_post_type['labels'] = (array) $post_type->labels; 
  864.  
  865. if ( in_array( 'cap', $fields ) ) { 
  866. $_post_type['cap'] = (array) $post_type->cap; 
  867. $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap; 
  868.  
  869. if ( in_array( 'menu', $fields ) ) { 
  870. $_post_type['menu_position'] = (int) $post_type->menu_position; 
  871. $_post_type['menu_icon'] = $post_type->menu_icon; 
  872. $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu; 
  873.  
  874. if ( in_array( 'taxonomies', $fields ) ) 
  875. $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' ); 
  876.  
  877. /** 
  878. * Filters XML-RPC-prepared date for the given post type. 
  879. * 
  880. * @since 3.4.0 
  881. * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. 
  882. * 
  883. * @param array $_post_type An array of post type data. 
  884. * @param WP_Post_Type $post_type Post type object. 
  885. */ 
  886. return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type ); 
  887.  
  888. /** 
  889. * Prepares media item data for return in an XML-RPC object. 
  890. * 
  891. * @access protected 
  892. * 
  893. * @param object $media_item The unprepared media item data. 
  894. * @param string $thumbnail_size The image size to use for the thumbnail URL. 
  895. * @return array The prepared media item data. 
  896. */ 
  897. protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) { 
  898. $_media_item = array( 
  899. 'attachment_id' => strval( $media_item->ID ),  
  900. 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),  
  901. 'parent' => $media_item->post_parent,  
  902. 'link' => wp_get_attachment_url( $media_item->ID ),  
  903. 'title' => $media_item->post_title,  
  904. 'caption' => $media_item->post_excerpt,  
  905. 'description' => $media_item->post_content,  
  906. 'metadata' => wp_get_attachment_metadata( $media_item->ID ),  
  907. 'type' => $media_item->post_mime_type 
  908. ); 
  909.  
  910. $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size ); 
  911. if ( $thumbnail_src ) 
  912. $_media_item['thumbnail'] = $thumbnail_src[0]; 
  913. else 
  914. $_media_item['thumbnail'] = $_media_item['link']; 
  915.  
  916. /** 
  917. * Filters XML-RPC-prepared data for the given media item. 
  918. * 
  919. * @since 3.4.0 
  920. * 
  921. * @param array $_media_item An array of media item data. 
  922. * @param object $media_item Media item object. 
  923. * @param string $thumbnail_size Image size. 
  924. */ 
  925. return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size ); 
  926.  
  927. /** 
  928. * Prepares page data for return in an XML-RPC object. 
  929. * 
  930. * @access protected 
  931. * 
  932. * @param object $page The unprepared page data. 
  933. * @return array The prepared page data. 
  934. */ 
  935. protected function _prepare_page( $page ) { 
  936. // Get all of the page content and link. 
  937. $full_page = get_extended( $page->post_content ); 
  938. $link = get_permalink( $page->ID ); 
  939.  
  940. // Get info the page parent if there is one. 
  941. $parent_title = ""; 
  942. if ( ! empty( $page->post_parent ) ) { 
  943. $parent = get_post( $page->post_parent ); 
  944. $parent_title = $parent->post_title; 
  945.  
  946. // Determine comment and ping settings. 
  947. $allow_comments = comments_open( $page->ID ) ? 1 : 0; 
  948. $allow_pings = pings_open( $page->ID ) ? 1 : 0; 
  949.  
  950. // Format page date. 
  951. $page_date = $this->_convert_date( $page->post_date ); 
  952. $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date ); 
  953.  
  954. // Pull the categories info together. 
  955. $categories = array(); 
  956. if ( is_object_in_taxonomy( 'page', 'category' ) ) { 
  957. foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) { 
  958. $categories[] = get_cat_name( $cat_id ); 
  959.  
  960. // Get the author info. 
  961. $author = get_userdata( $page->post_author ); 
  962.  
  963. $page_template = get_page_template_slug( $page->ID ); 
  964. if ( empty( $page_template ) ) 
  965. $page_template = 'default'; 
  966.  
  967. $_page = array( 
  968. 'dateCreated' => $page_date,  
  969. 'userid' => $page->post_author,  
  970. 'page_id' => $page->ID,  
  971. 'page_status' => $page->post_status,  
  972. 'description' => $full_page['main'],  
  973. 'title' => $page->post_title,  
  974. 'link' => $link,  
  975. 'permaLink' => $link,  
  976. 'categories' => $categories,  
  977. 'excerpt' => $page->post_excerpt,  
  978. 'text_more' => $full_page['extended'],  
  979. 'mt_allow_comments' => $allow_comments,  
  980. 'mt_allow_pings' => $allow_pings,  
  981. 'wp_slug' => $page->post_name,  
  982. 'wp_password' => $page->post_password,  
  983. 'wp_author' => $author->display_name,  
  984. 'wp_page_parent_id' => $page->post_parent,  
  985. 'wp_page_parent_title' => $parent_title,  
  986. 'wp_page_order' => $page->menu_order,  
  987. 'wp_author_id' => (string) $author->ID,  
  988. 'wp_author_display_name' => $author->display_name,  
  989. 'date_created_gmt' => $page_date_gmt,  
  990. 'custom_fields' => $this->get_custom_fields( $page->ID ),  
  991. 'wp_page_template' => $page_template 
  992. ); 
  993.  
  994. /** 
  995. * Filters XML-RPC-prepared data for the given page. 
  996. * 
  997. * @since 3.4.0 
  998. * 
  999. * @param array $_page An array of page data. 
  1000. * @param WP_Post $page Page object. 
  1001. */ 
  1002. return apply_filters( 'xmlrpc_prepare_page', $_page, $page ); 
  1003.  
  1004. /** 
  1005. * Prepares comment data for return in an XML-RPC object. 
  1006. * 
  1007. * @access protected 
  1008. * 
  1009. * @param object $comment The unprepared comment data. 
  1010. * @return array The prepared comment data. 
  1011. */ 
  1012. protected function _prepare_comment( $comment ) { 
  1013. // Format page date. 
  1014. $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date ); 
  1015.  
  1016. if ( '0' == $comment->comment_approved ) { 
  1017. $comment_status = 'hold'; 
  1018. } elseif ( 'spam' == $comment->comment_approved ) { 
  1019. $comment_status = 'spam'; 
  1020. } elseif ( '1' == $comment->comment_approved ) { 
  1021. $comment_status = 'approve'; 
  1022. } else { 
  1023. $comment_status = $comment->comment_approved; 
  1024. $_comment = array( 
  1025. 'date_created_gmt' => $comment_date_gmt,  
  1026. 'user_id' => $comment->user_id,  
  1027. 'comment_id' => $comment->comment_ID,  
  1028. 'parent' => $comment->comment_parent,  
  1029. 'status' => $comment_status,  
  1030. 'content' => $comment->comment_content,  
  1031. 'link' => get_comment_link($comment),  
  1032. 'post_id' => $comment->comment_post_ID,  
  1033. 'post_title' => get_the_title($comment->comment_post_ID),  
  1034. 'author' => $comment->comment_author,  
  1035. 'author_url' => $comment->comment_author_url,  
  1036. 'author_email' => $comment->comment_author_email,  
  1037. 'author_ip' => $comment->comment_author_IP,  
  1038. 'type' => $comment->comment_type,  
  1039. ); 
  1040.  
  1041. /** 
  1042. * Filters XML-RPC-prepared data for the given comment. 
  1043. * 
  1044. * @since 3.4.0 
  1045. * 
  1046. * @param array $_comment An array of prepared comment data. 
  1047. * @param WP_Comment $comment Comment object. 
  1048. */ 
  1049. return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment ); 
  1050.  
  1051. /** 
  1052. * Prepares user data for return in an XML-RPC object. 
  1053. * 
  1054. * @access protected 
  1055. * 
  1056. * @param WP_User $user The unprepared user object. 
  1057. * @param array $fields The subset of user fields to return. 
  1058. * @return array The prepared user data. 
  1059. */ 
  1060. protected function _prepare_user( $user, $fields ) { 
  1061. $_user = array( 'user_id' => strval( $user->ID ) ); 
  1062.  
  1063. $user_fields = array( 
  1064. 'username' => $user->user_login,  
  1065. 'first_name' => $user->user_firstname,  
  1066. 'last_name' => $user->user_lastname,  
  1067. 'registered' => $this->_convert_date( $user->user_registered ),  
  1068. 'bio' => $user->user_description,  
  1069. 'email' => $user->user_email,  
  1070. 'nickname' => $user->nickname,  
  1071. 'nicename' => $user->user_nicename,  
  1072. 'url' => $user->user_url,  
  1073. 'display_name' => $user->display_name,  
  1074. 'roles' => $user->roles,  
  1075. ); 
  1076.  
  1077. if ( in_array( 'all', $fields ) ) { 
  1078. $_user = array_merge( $_user, $user_fields ); 
  1079. } else { 
  1080. if ( in_array( 'basic', $fields ) ) { 
  1081. $basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' ); 
  1082. $fields = array_merge( $fields, $basic_fields ); 
  1083. $requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) ); 
  1084. $_user = array_merge( $_user, $requested_fields ); 
  1085.  
  1086. /** 
  1087. * Filters XML-RPC-prepared data for the given user. 
  1088. * 
  1089. * @since 3.5.0 
  1090. * 
  1091. * @param array $_user An array of user data. 
  1092. * @param WP_User $user User object. 
  1093. * @param array $fields An array of user fields. 
  1094. */ 
  1095. return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields ); 
  1096.  
  1097. /** 
  1098. * Create a new post for any registered post type. 
  1099. * 
  1100. * @since 3.4.0 
  1101. * 
  1102. * @link https://en.wikipedia.org/wiki/RSS_enclosure for information on RSS enclosures. 
  1103. * 
  1104. * @param array $args { 
  1105. * Method arguments. Note: top-level arguments must be ordered as documented. 
  1106. * 
  1107. * @type int $blog_id Blog ID (unused). 
  1108. * @type string $username Username. 
  1109. * @type string $password Password. 
  1110. * @type array $content_struct { 
  1111. * Content struct for adding a new post. See wp_insert_post() for information on 
  1112. * additional post fields 
  1113. * 
  1114. * @type string $post_type Post type. Default 'post'. 
  1115. * @type string $post_status Post status. Default 'draft' 
  1116. * @type string $post_title Post title. 
  1117. * @type int $post_author Post author ID. 
  1118. * @type string $post_excerpt Post excerpt. 
  1119. * @type string $post_content Post content. 
  1120. * @type string $post_date_gmt Post date in GMT. 
  1121. * @type string $post_date Post date. 
  1122. * @type string $post_password Post password (20-character limit). 
  1123. * @type string $comment_status Post comment enabled status. Accepts 'open' or 'closed'. 
  1124. * @type string $ping_status Post ping status. Accepts 'open' or 'closed'. 
  1125. * @type bool $sticky Whether the post should be sticky. Automatically false if 
  1126. * `$post_status` is 'private'. 
  1127. * @type int $post_thumbnail ID of an image to use as the post thumbnail/featured image. 
  1128. * @type array $custom_fields Array of meta key/value pairs to add to the post. 
  1129. * @type array $terms Associative array with taxonomy names as keys and arrays 
  1130. * of term IDs as values. 
  1131. * @type array $terms_names Associative array with taxonomy names as keys and arrays 
  1132. * of term names as values. 
  1133. * @type array $enclosure { 
  1134. * Array of feed enclosure data to add to post meta. 
  1135. * 
  1136. * @type string $url URL for the feed enclosure. 
  1137. * @type int $length Size in bytes of the enclosure. 
  1138. * @type string $type Mime-type for the enclosure. 
  1139. * } 
  1140. * } 
  1141. * } 
  1142. * @return int|IXR_Error Post ID on success, IXR_Error instance otherwise. 
  1143. */ 
  1144. public function wp_newPost( $args ) { 
  1145. if ( ! $this->minimum_args( $args, 4 ) ) 
  1146. return $this->error; 
  1147.  
  1148. $this->escape( $args ); 
  1149.  
  1150. $username = $args[1]; 
  1151. $password = $args[2]; 
  1152. $content_struct = $args[3]; 
  1153.  
  1154. if ( ! $user = $this->login( $username, $password ) ) 
  1155. return $this->error; 
  1156.  
  1157. // convert the date field back to IXR form 
  1158. if ( isset( $content_struct['post_date'] ) && ! ( $content_struct['post_date'] instanceof IXR_Date ) ) { 
  1159. $content_struct['post_date'] = $this->_convert_date( $content_struct['post_date'] ); 
  1160.  
  1161. // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,  
  1162. // since _insert_post will ignore the non-GMT date if the GMT date is set 
  1163. if ( isset( $content_struct['post_date_gmt'] ) && ! ( $content_struct['post_date_gmt'] instanceof IXR_Date ) ) { 
  1164. if ( $content_struct['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) ) { 
  1165. unset( $content_struct['post_date_gmt'] ); 
  1166. } else { 
  1167. $content_struct['post_date_gmt'] = $this->_convert_date( $content_struct['post_date_gmt'] ); 
  1168.  
  1169. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1170. do_action( 'xmlrpc_call', 'wp.newPost' ); 
  1171.  
  1172. unset( $content_struct['ID'] ); 
  1173.  
  1174. return $this->_insert_post( $user, $content_struct ); 
  1175.  
  1176. /** 
  1177. * Helper method for filtering out elements from an array. 
  1178. * 
  1179. * @since 3.4.0 
  1180. * 
  1181. * @param int $count Number to compare to one. 
  1182. */ 
  1183. private function _is_greater_than_one( $count ) { 
  1184. return $count > 1; 
  1185.  
  1186. /** 
  1187. * Encapsulate the logic for sticking a post 
  1188. * and determining if the user has permission to do so 
  1189. * 
  1190. * @since 4.3.0 
  1191. * @access private 
  1192. * 
  1193. * @param array $post_data 
  1194. * @param bool $update 
  1195. * @return void|IXR_Error 
  1196. */ 
  1197. private function _toggle_sticky( $post_data, $update = false ) { 
  1198. $post_type = get_post_type_object( $post_data['post_type'] ); 
  1199.  
  1200. // Private and password-protected posts cannot be stickied. 
  1201. if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) { 
  1202. // Error if the client tried to stick the post, otherwise, silently unstick. 
  1203. if ( ! empty( $post_data['sticky'] ) ) { 
  1204. return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) ); 
  1205.  
  1206. if ( $update ) { 
  1207. unstick_post( $post_data['ID'] ); 
  1208. } elseif ( isset( $post_data['sticky'] ) ) { 
  1209. if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) { 
  1210. return new IXR_Error( 401, __( 'Sorry, you are not allowed to make posts sticky.' ) ); 
  1211.  
  1212. $sticky = wp_validate_boolean( $post_data['sticky'] ); 
  1213. if ( $sticky ) { 
  1214. stick_post( $post_data['ID'] ); 
  1215. } else { 
  1216. unstick_post( $post_data['ID'] ); 
  1217.  
  1218. /** 
  1219. * Helper method for wp_newPost() and wp_editPost(), containing shared logic. 
  1220. * 
  1221. * @since 3.4.0 
  1222. * @access protected 
  1223. * 
  1224. * @see wp_insert_post() 
  1225. * 
  1226. * @param WP_User $user The post author if post_author isn't set in $content_struct. 
  1227. * @param array|IXR_Error $content_struct Post data to insert. 
  1228. * @return IXR_Error|string 
  1229. */ 
  1230. protected function _insert_post( $user, $content_struct ) { 
  1231. $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0,  
  1232. 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' ); 
  1233.  
  1234. $post_data = wp_parse_args( $content_struct, $defaults ); 
  1235.  
  1236. $post_type = get_post_type_object( $post_data['post_type'] ); 
  1237. if ( ! $post_type ) 
  1238. return new IXR_Error( 403, __( 'Invalid post type.' ) ); 
  1239.  
  1240. $update = ! empty( $post_data['ID'] ); 
  1241.  
  1242. if ( $update ) { 
  1243. if ( ! get_post( $post_data['ID'] ) ) 
  1244. return new IXR_Error( 401, __( 'Invalid post ID.' ) ); 
  1245. if ( ! current_user_can( 'edit_post', $post_data['ID'] ) ) 
  1246. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  1247. if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) ) 
  1248. return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); 
  1249. } else { 
  1250. if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) ) 
  1251. return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) ); 
  1252.  
  1253. switch ( $post_data['post_status'] ) { 
  1254. case 'draft': 
  1255. case 'pending': 
  1256. break; 
  1257. case 'private': 
  1258. if ( ! current_user_can( $post_type->cap->publish_posts ) ) 
  1259. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type.' ) ); 
  1260. break; 
  1261. case 'publish': 
  1262. case 'future': 
  1263. if ( ! current_user_can( $post_type->cap->publish_posts ) ) 
  1264. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type.' ) ); 
  1265. break; 
  1266. default: 
  1267. if ( ! get_post_status_object( $post_data['post_status'] ) ) 
  1268. $post_data['post_status'] = 'draft'; 
  1269. break; 
  1270.  
  1271. if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) 
  1272. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type.' ) ); 
  1273.  
  1274. $post_data['post_author'] = absint( $post_data['post_author'] ); 
  1275. if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) { 
  1276. if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) 
  1277. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); 
  1278.  
  1279. $author = get_userdata( $post_data['post_author'] ); 
  1280.  
  1281. if ( ! $author ) 
  1282. return new IXR_Error( 404, __( 'Invalid author ID.' ) ); 
  1283. } else { 
  1284. $post_data['post_author'] = $user->ID; 
  1285.  
  1286. if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' ) 
  1287. unset( $post_data['comment_status'] ); 
  1288.  
  1289. if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' ) 
  1290. unset( $post_data['ping_status'] ); 
  1291.  
  1292. // Do some timestamp voodoo. 
  1293. if ( ! empty( $post_data['post_date_gmt'] ) ) { 
  1294. // We know this is supposed to be GMT, so we're going to slap that Z on there by force. 
  1295. $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z'; 
  1296. } elseif ( ! empty( $post_data['post_date'] ) ) { 
  1297. $dateCreated = $post_data['post_date']->getIso(); 
  1298.  
  1299. // Default to not flagging the post date to be edited unless it's intentional. 
  1300. $post_data['edit_date'] = false; 
  1301.  
  1302. if ( ! empty( $dateCreated ) ) { 
  1303. $post_data['post_date'] = get_date_from_gmt( iso8601_to_datetime( $dateCreated ) ); 
  1304. $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'GMT' ); 
  1305.  
  1306. // Flag the post date to be edited. 
  1307. $post_data['edit_date'] = true; 
  1308.  
  1309. if ( ! isset( $post_data['ID'] ) ) 
  1310. $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID; 
  1311. $post_ID = $post_data['ID']; 
  1312.  
  1313. if ( $post_data['post_type'] == 'post' ) { 
  1314. $error = $this->_toggle_sticky( $post_data, $update ); 
  1315. if ( $error ) { 
  1316. return $error; 
  1317.  
  1318. if ( isset( $post_data['post_thumbnail'] ) ) { 
  1319. // empty value deletes, non-empty value adds/updates. 
  1320. if ( ! $post_data['post_thumbnail'] ) 
  1321. delete_post_thumbnail( $post_ID ); 
  1322. elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) ) 
  1323. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  1324. set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ); 
  1325. unset( $content_struct['post_thumbnail'] ); 
  1326.  
  1327. if ( isset( $post_data['custom_fields'] ) ) 
  1328. $this->set_custom_fields( $post_ID, $post_data['custom_fields'] ); 
  1329.  
  1330. if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) { 
  1331. $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' ); 
  1332.  
  1333. // Accumulate term IDs from terms and terms_names. 
  1334. $terms = array(); 
  1335.  
  1336. // First validate the terms specified by ID. 
  1337. if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) { 
  1338. $taxonomies = array_keys( $post_data['terms'] ); 
  1339.  
  1340. // Validating term ids. 
  1341. foreach ( $taxonomies as $taxonomy ) { 
  1342. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) ) 
  1343. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); 
  1344.  
  1345. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) ) 
  1346. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); 
  1347.  
  1348. $term_ids = $post_data['terms'][$taxonomy]; 
  1349. $terms[ $taxonomy ] = array(); 
  1350. foreach ( $term_ids as $term_id ) { 
  1351. $term = get_term_by( 'id', $term_id, $taxonomy ); 
  1352.  
  1353. if ( ! $term ) 
  1354. return new IXR_Error( 403, __( 'Invalid term ID.' ) ); 
  1355.  
  1356. $terms[$taxonomy][] = (int) $term_id; 
  1357.  
  1358. // Now validate terms specified by name. 
  1359. if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) { 
  1360. $taxonomies = array_keys( $post_data['terms_names'] ); 
  1361.  
  1362. foreach ( $taxonomies as $taxonomy ) { 
  1363. if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) ) 
  1364. return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); 
  1365.  
  1366. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) ) 
  1367. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); 
  1368.  
  1369. /** 
  1370. * For hierarchical taxonomies, we can't assign a term when multiple terms 
  1371. * in the hierarchy share the same name. 
  1372. */ 
  1373. $ambiguous_terms = array(); 
  1374. if ( is_taxonomy_hierarchical( $taxonomy ) ) { 
  1375. $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) ); 
  1376.  
  1377. // Count the number of terms with the same name. 
  1378. $tax_term_names_count = array_count_values( $tax_term_names ); 
  1379.  
  1380. // Filter out non-ambiguous term names. 
  1381. $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') ); 
  1382.  
  1383. $ambiguous_terms = array_keys( $ambiguous_tax_term_counts ); 
  1384.  
  1385. $term_names = $post_data['terms_names'][$taxonomy]; 
  1386. foreach ( $term_names as $term_name ) { 
  1387. if ( in_array( $term_name, $ambiguous_terms ) ) 
  1388. return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) ); 
  1389.  
  1390. $term = get_term_by( 'name', $term_name, $taxonomy ); 
  1391.  
  1392. if ( ! $term ) { 
  1393. // Term doesn't exist, so check that the user is allowed to create new terms. 
  1394. if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) ) 
  1395. return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) ); 
  1396.  
  1397. // Create the new term. 
  1398. $term_info = wp_insert_term( $term_name, $taxonomy ); 
  1399. if ( is_wp_error( $term_info ) ) 
  1400. return new IXR_Error( 500, $term_info->get_error_message() ); 
  1401.  
  1402. $terms[$taxonomy][] = (int) $term_info['term_id']; 
  1403. } else { 
  1404. $terms[$taxonomy][] = (int) $term->term_id; 
  1405.  
  1406. $post_data['tax_input'] = $terms; 
  1407. unset( $post_data['terms'], $post_data['terms_names'] ); 
  1408. } else { 
  1409. // Do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names'. 
  1410. unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] ); 
  1411.  
  1412. if ( isset( $post_data['post_format'] ) ) { 
  1413. $format = set_post_format( $post_ID, $post_data['post_format'] ); 
  1414.  
  1415. if ( is_wp_error( $format ) ) 
  1416. return new IXR_Error( 500, $format->get_error_message() ); 
  1417.  
  1418. unset( $post_data['post_format'] ); 
  1419.  
  1420. // Handle enclosures. 
  1421. $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null; 
  1422. $this->add_enclosure_if_new( $post_ID, $enclosure ); 
  1423.  
  1424. $this->attach_uploads( $post_ID, $post_data['post_content'] ); 
  1425.  
  1426. /** 
  1427. * Filters post data array to be inserted via XML-RPC. 
  1428. * 
  1429. * @since 3.4.0 
  1430. * 
  1431. * @param array $post_data Parsed array of post data. 
  1432. * @param array $content_struct Post data array. 
  1433. */ 
  1434. $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct ); 
  1435.  
  1436. $post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true ); 
  1437. if ( is_wp_error( $post_ID ) ) 
  1438. return new IXR_Error( 500, $post_ID->get_error_message() ); 
  1439.  
  1440. if ( ! $post_ID ) 
  1441. return new IXR_Error( 401, __( 'Sorry, your entry could not be posted.' ) ); 
  1442.  
  1443. return strval( $post_ID ); 
  1444.  
  1445. /** 
  1446. * Edit a post for any registered post type. 
  1447. * 
  1448. * The $content_struct parameter only needs to contain fields that 
  1449. * should be changed. All other fields will retain their existing values. 
  1450. * 
  1451. * @since 3.4.0 
  1452. * 
  1453. * @param array $args { 
  1454. * Method arguments. Note: arguments must be ordered as documented. 
  1455. * 
  1456. * @type int $blog_id Blog ID (unused). 
  1457. * @type string $username Username. 
  1458. * @type string $password Password. 
  1459. * @type int $post_id Post ID. 
  1460. * @type array $content_struct Extra content arguments. 
  1461. * } 
  1462. * @return true|IXR_Error True on success, IXR_Error on failure. 
  1463. */ 
  1464. public function wp_editPost( $args ) { 
  1465. if ( ! $this->minimum_args( $args, 5 ) ) 
  1466. return $this->error; 
  1467.  
  1468. $this->escape( $args ); 
  1469.  
  1470. $username = $args[1]; 
  1471. $password = $args[2]; 
  1472. $post_id = (int) $args[3]; 
  1473. $content_struct = $args[4]; 
  1474.  
  1475. if ( ! $user = $this->login( $username, $password ) ) 
  1476. return $this->error; 
  1477.  
  1478. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1479. do_action( 'xmlrpc_call', 'wp.editPost' ); 
  1480.  
  1481. $post = get_post( $post_id, ARRAY_A ); 
  1482.  
  1483. if ( empty( $post['ID'] ) ) 
  1484. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  1485.  
  1486. if ( isset( $content_struct['if_not_modified_since'] ) ) { 
  1487. // If the post has been modified since the date provided, return an error. 
  1488. if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) { 
  1489. return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) ); 
  1490.  
  1491. // Convert the date field back to IXR form. 
  1492. $post['post_date'] = $this->_convert_date( $post['post_date'] ); 
  1493.  
  1494. /** 
  1495. * Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,  
  1496. * since _insert_post() will ignore the non-GMT date if the GMT date is set. 
  1497. */ 
  1498. if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) ) 
  1499. unset( $post['post_date_gmt'] ); 
  1500. else 
  1501. $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] ); 
  1502.  
  1503. $this->escape( $post ); 
  1504. $merged_content_struct = array_merge( $post, $content_struct ); 
  1505.  
  1506. $retval = $this->_insert_post( $user, $merged_content_struct ); 
  1507. if ( $retval instanceof IXR_Error ) 
  1508. return $retval; 
  1509.  
  1510. return true; 
  1511.  
  1512. /** 
  1513. * Delete a post for any registered post type. 
  1514. * 
  1515. * @since 3.4.0 
  1516. * 
  1517. * @see wp_delete_post() 
  1518. * 
  1519. * @param array $args { 
  1520. * Method arguments. Note: arguments must be ordered as documented. 
  1521. * 
  1522. * @type int $blog_id Blog ID (unused). 
  1523. * @type string $username Username. 
  1524. * @type string $password Password. 
  1525. * @type int $post_id Post ID. 
  1526. * } 
  1527. * @return true|IXR_Error True on success, IXR_Error instance on failure. 
  1528. */ 
  1529. public function wp_deletePost( $args ) { 
  1530. if ( ! $this->minimum_args( $args, 4 ) ) 
  1531. return $this->error; 
  1532.  
  1533. $this->escape( $args ); 
  1534.  
  1535. $username = $args[1]; 
  1536. $password = $args[2]; 
  1537. $post_id = (int) $args[3]; 
  1538.  
  1539. if ( ! $user = $this->login( $username, $password ) ) 
  1540. return $this->error; 
  1541.  
  1542. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1543. do_action( 'xmlrpc_call', 'wp.deletePost' ); 
  1544.  
  1545. $post = get_post( $post_id, ARRAY_A ); 
  1546. if ( empty( $post['ID'] ) ) { 
  1547. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  1548.  
  1549. if ( ! current_user_can( 'delete_post', $post_id ) ) { 
  1550. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); 
  1551.  
  1552. $result = wp_delete_post( $post_id ); 
  1553.  
  1554. if ( ! $result ) { 
  1555. return new IXR_Error( 500, __( 'The post cannot be deleted.' ) ); 
  1556.  
  1557. return true; 
  1558.  
  1559. /** 
  1560. * Retrieve a post. 
  1561. * 
  1562. * @since 3.4.0 
  1563. * 
  1564. * The optional $fields parameter specifies what fields will be included 
  1565. * in the response array. This should be a list of field names. 'post_id' will 
  1566. * always be included in the response regardless of the value of $fields. 
  1567. * 
  1568. * Instead of, or in addition to, individual field names, conceptual group 
  1569. * names can be used to specify multiple fields. The available conceptual 
  1570. * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',  
  1571. * and 'enclosure'. 
  1572. * 
  1573. * @see get_post() 
  1574. * 
  1575. * @param array $args { 
  1576. * Method arguments. Note: arguments must be ordered as documented. 
  1577. * 
  1578. * @type int $blog_id Blog ID (unused). 
  1579. * @type string $username Username. 
  1580. * @type string $password Password. 
  1581. * @type int $post_id Post ID. 
  1582. * @type array $fields The subset of post type fields to return. 
  1583. * } 
  1584. * @return array|IXR_Error Array contains (based on $fields parameter): 
  1585. * - 'post_id' 
  1586. * - 'post_title' 
  1587. * - 'post_date' 
  1588. * - 'post_date_gmt' 
  1589. * - 'post_modified' 
  1590. * - 'post_modified_gmt' 
  1591. * - 'post_status' 
  1592. * - 'post_type' 
  1593. * - 'post_name' 
  1594. * - 'post_author' 
  1595. * - 'post_password' 
  1596. * - 'post_excerpt' 
  1597. * - 'post_content' 
  1598. * - 'link' 
  1599. * - 'comment_status' 
  1600. * - 'ping_status' 
  1601. * - 'sticky' 
  1602. * - 'custom_fields' 
  1603. * - 'terms' 
  1604. * - 'categories' 
  1605. * - 'tags' 
  1606. * - 'enclosure' 
  1607. */ 
  1608. public function wp_getPost( $args ) { 
  1609. if ( ! $this->minimum_args( $args, 4 ) ) 
  1610. return $this->error; 
  1611.  
  1612. $this->escape( $args ); 
  1613.  
  1614. $username = $args[1]; 
  1615. $password = $args[2]; 
  1616. $post_id = (int) $args[3]; 
  1617.  
  1618. if ( isset( $args[4] ) ) { 
  1619. $fields = $args[4]; 
  1620. } else { 
  1621. /** 
  1622. * Filters the list of post query fields used by the given XML-RPC method. 
  1623. * 
  1624. * @since 3.4.0 
  1625. * 
  1626. * @param array $fields Array of post fields. Default array contains 'post', 'terms', and 'custom_fields'. 
  1627. * @param string $method Method name. 
  1628. */ 
  1629. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' ); 
  1630.  
  1631. if ( ! $user = $this->login( $username, $password ) ) 
  1632. return $this->error; 
  1633.  
  1634. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1635. do_action( 'xmlrpc_call', 'wp.getPost' ); 
  1636.  
  1637. $post = get_post( $post_id, ARRAY_A ); 
  1638.  
  1639. if ( empty( $post['ID'] ) ) 
  1640. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  1641.  
  1642. if ( ! current_user_can( 'edit_post', $post_id ) ) 
  1643. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  1644.  
  1645. return $this->_prepare_post( $post, $fields ); 
  1646.  
  1647. /** 
  1648. * Retrieve posts. 
  1649. * 
  1650. * @since 3.4.0 
  1651. * 
  1652. * @see wp_get_recent_posts() 
  1653. * @see wp_getPost() for more on `$fields` 
  1654. * @see get_posts() for more on `$filter` values 
  1655. * 
  1656. * @param array $args { 
  1657. * Method arguments. Note: arguments must be ordered as documented. 
  1658. * 
  1659. * @type int $blog_id Blog ID (unused). 
  1660. * @type string $username Username. 
  1661. * @type string $password Password. 
  1662. * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'post_type',  
  1663. * 'post_status', 'number', 'offset', 'orderby', 's', and 'order'. 
  1664. * Default empty array. 
  1665. * @type array $fields Optional. The subset of post type fields to return in the response array. 
  1666. * } 
  1667. * @return array|IXR_Error Array contains a collection of posts. 
  1668. */ 
  1669. public function wp_getPosts( $args ) { 
  1670. if ( ! $this->minimum_args( $args, 3 ) ) 
  1671. return $this->error; 
  1672.  
  1673. $this->escape( $args ); 
  1674.  
  1675. $username = $args[1]; 
  1676. $password = $args[2]; 
  1677. $filter = isset( $args[3] ) ? $args[3] : array(); 
  1678.  
  1679. if ( isset( $args[4] ) ) { 
  1680. $fields = $args[4]; 
  1681. } else { 
  1682. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1683. $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' ); 
  1684.  
  1685. if ( ! $user = $this->login( $username, $password ) ) 
  1686. return $this->error; 
  1687.  
  1688. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1689. do_action( 'xmlrpc_call', 'wp.getPosts' ); 
  1690.  
  1691. $query = array(); 
  1692.  
  1693. if ( isset( $filter['post_type'] ) ) { 
  1694. $post_type = get_post_type_object( $filter['post_type'] ); 
  1695. if ( ! ( (bool) $post_type ) ) 
  1696. return new IXR_Error( 403, __( 'Invalid post type.' ) ); 
  1697. } else { 
  1698. $post_type = get_post_type_object( 'post' ); 
  1699.  
  1700. if ( ! current_user_can( $post_type->cap->edit_posts ) ) 
  1701. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); 
  1702.  
  1703. $query['post_type'] = $post_type->name; 
  1704.  
  1705. if ( isset( $filter['post_status'] ) ) 
  1706. $query['post_status'] = $filter['post_status']; 
  1707.  
  1708. if ( isset( $filter['number'] ) ) 
  1709. $query['numberposts'] = absint( $filter['number'] ); 
  1710.  
  1711. if ( isset( $filter['offset'] ) ) 
  1712. $query['offset'] = absint( $filter['offset'] ); 
  1713.  
  1714. if ( isset( $filter['orderby'] ) ) { 
  1715. $query['orderby'] = $filter['orderby']; 
  1716.  
  1717. if ( isset( $filter['order'] ) ) 
  1718. $query['order'] = $filter['order']; 
  1719.  
  1720. if ( isset( $filter['s'] ) ) { 
  1721. $query['s'] = $filter['s']; 
  1722.  
  1723. $posts_list = wp_get_recent_posts( $query ); 
  1724.  
  1725. if ( ! $posts_list ) 
  1726. return array(); 
  1727.  
  1728. // Holds all the posts data. 
  1729. $struct = array(); 
  1730.  
  1731. foreach ( $posts_list as $post ) { 
  1732. if ( ! current_user_can( 'edit_post', $post['ID'] ) ) 
  1733. continue; 
  1734.  
  1735. $struct[] = $this->_prepare_post( $post, $fields ); 
  1736.  
  1737. return $struct; 
  1738.  
  1739. /** 
  1740. * Create a new term. 
  1741. * 
  1742. * @since 3.4.0 
  1743. * 
  1744. * @see wp_insert_term() 
  1745. * 
  1746. * @param array $args { 
  1747. * Method arguments. Note: arguments must be ordered as documented. 
  1748. * 
  1749. * @type int $blog_id Blog ID (unused). 
  1750. * @type string $username Username. 
  1751. * @type string $password Password. 
  1752. * @type array $content_struct Content struct for adding a new term. The struct must contain 
  1753. * the term 'name' and 'taxonomy'. Optional accepted values include 
  1754. * 'parent', 'description', and 'slug'. 
  1755. * } 
  1756. * @return int|IXR_Error The term ID on success, or an IXR_Error object on failure. 
  1757. */ 
  1758. public function wp_newTerm( $args ) { 
  1759. if ( ! $this->minimum_args( $args, 4 ) ) 
  1760. return $this->error; 
  1761.  
  1762. $this->escape( $args ); 
  1763.  
  1764. $username = $args[1]; 
  1765. $password = $args[2]; 
  1766. $content_struct = $args[3]; 
  1767.  
  1768. if ( ! $user = $this->login( $username, $password ) ) 
  1769. return $this->error; 
  1770.  
  1771. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1772. do_action( 'xmlrpc_call', 'wp.newTerm' ); 
  1773.  
  1774. if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) 
  1775. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  1776.  
  1777. $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); 
  1778.  
  1779. if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) { 
  1780. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create terms in this taxonomy.' ) ); 
  1781.  
  1782. $taxonomy = (array) $taxonomy; 
  1783.  
  1784. // hold the data of the term 
  1785. $term_data = array(); 
  1786.  
  1787. $term_data['name'] = trim( $content_struct['name'] ); 
  1788. if ( empty( $term_data['name'] ) ) 
  1789. return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); 
  1790.  
  1791. if ( isset( $content_struct['parent'] ) ) { 
  1792. if ( ! $taxonomy['hierarchical'] ) 
  1793. return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) ); 
  1794.  
  1795. $parent_term_id = (int) $content_struct['parent']; 
  1796. $parent_term = get_term( $parent_term_id , $taxonomy['name'] ); 
  1797.  
  1798. if ( is_wp_error( $parent_term ) ) 
  1799. return new IXR_Error( 500, $parent_term->get_error_message() ); 
  1800.  
  1801. if ( ! $parent_term ) 
  1802. return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); 
  1803.  
  1804. $term_data['parent'] = $content_struct['parent']; 
  1805.  
  1806. if ( isset( $content_struct['description'] ) ) 
  1807. $term_data['description'] = $content_struct['description']; 
  1808.  
  1809. if ( isset( $content_struct['slug'] ) ) 
  1810. $term_data['slug'] = $content_struct['slug']; 
  1811.  
  1812. $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data ); 
  1813.  
  1814. if ( is_wp_error( $term ) ) 
  1815. return new IXR_Error( 500, $term->get_error_message() ); 
  1816.  
  1817. if ( ! $term ) 
  1818. return new IXR_Error( 500, __( 'Sorry, your term could not be created.' ) ); 
  1819.  
  1820. return strval( $term['term_id'] ); 
  1821.  
  1822. /** 
  1823. * Edit a term. 
  1824. * 
  1825. * @since 3.4.0 
  1826. * 
  1827. * @see wp_update_term() 
  1828. * 
  1829. * @param array $args { 
  1830. * Method arguments. Note: arguments must be ordered as documented. 
  1831. * 
  1832. * @type int $blog_id Blog ID (unused). 
  1833. * @type string $username Username. 
  1834. * @type string $password Password. 
  1835. * @type int $term_id Term ID. 
  1836. * @type array $content_struct Content struct for editing a term. The struct must contain the 
  1837. * term ''taxonomy'. Optional accepted values include 'name', 'parent',  
  1838. * 'description', and 'slug'. 
  1839. * } 
  1840. * @return true|IXR_Error True on success, IXR_Error instance on failure. 
  1841. */ 
  1842. public function wp_editTerm( $args ) { 
  1843. if ( ! $this->minimum_args( $args, 5 ) ) 
  1844. return $this->error; 
  1845.  
  1846. $this->escape( $args ); 
  1847.  
  1848. $username = $args[1]; 
  1849. $password = $args[2]; 
  1850. $term_id = (int) $args[3]; 
  1851. $content_struct = $args[4]; 
  1852.  
  1853. if ( ! $user = $this->login( $username, $password ) ) 
  1854. return $this->error; 
  1855.  
  1856. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1857. do_action( 'xmlrpc_call', 'wp.editTerm' ); 
  1858.  
  1859. if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) 
  1860. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  1861.  
  1862. $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); 
  1863.  
  1864. $taxonomy = (array) $taxonomy; 
  1865.  
  1866. // hold the data of the term 
  1867. $term_data = array(); 
  1868.  
  1869. $term = get_term( $term_id , $content_struct['taxonomy'] ); 
  1870.  
  1871. if ( is_wp_error( $term ) ) 
  1872. return new IXR_Error( 500, $term->get_error_message() ); 
  1873.  
  1874. if ( ! $term ) 
  1875. return new IXR_Error( 404, __( 'Invalid term ID.' ) ); 
  1876.  
  1877. if ( ! current_user_can( 'edit_term', $term_id ) ) { 
  1878. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this term.' ) ); 
  1879.  
  1880. if ( isset( $content_struct['name'] ) ) { 
  1881. $term_data['name'] = trim( $content_struct['name'] ); 
  1882.  
  1883. if ( empty( $term_data['name'] ) ) 
  1884. return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); 
  1885.  
  1886. if ( ! empty( $content_struct['parent'] ) ) { 
  1887. if ( ! $taxonomy['hierarchical'] ) 
  1888. return new IXR_Error( 403, __( "This taxonomy is not hierarchical so you can't set a parent." ) ); 
  1889.  
  1890. $parent_term_id = (int) $content_struct['parent']; 
  1891. $parent_term = get_term( $parent_term_id , $taxonomy['name'] ); 
  1892.  
  1893. if ( is_wp_error( $parent_term ) ) 
  1894. return new IXR_Error( 500, $parent_term->get_error_message() ); 
  1895.  
  1896. if ( ! $parent_term ) 
  1897. return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); 
  1898.  
  1899. $term_data['parent'] = $content_struct['parent']; 
  1900.  
  1901. if ( isset( $content_struct['description'] ) ) 
  1902. $term_data['description'] = $content_struct['description']; 
  1903.  
  1904. if ( isset( $content_struct['slug'] ) ) 
  1905. $term_data['slug'] = $content_struct['slug']; 
  1906.  
  1907. $term = wp_update_term( $term_id , $taxonomy['name'] , $term_data ); 
  1908.  
  1909. if ( is_wp_error( $term ) ) 
  1910. return new IXR_Error( 500, $term->get_error_message() ); 
  1911.  
  1912. if ( ! $term ) 
  1913. return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) ); 
  1914.  
  1915. return true; 
  1916.  
  1917. /** 
  1918. * Delete a term. 
  1919. * 
  1920. * @since 3.4.0 
  1921. * 
  1922. * @see wp_delete_term() 
  1923. * 
  1924. * @param array $args { 
  1925. * Method arguments. Note: arguments must be ordered as documented. 
  1926. * 
  1927. * @type int $blog_id Blog ID (unused). 
  1928. * @type string $username Username. 
  1929. * @type string $password Password. 
  1930. * @type string $taxnomy_name Taxonomy name. 
  1931. * @type int $term_id Term ID. 
  1932. * } 
  1933. * @return bool|IXR_Error True on success, IXR_Error instance on failure. 
  1934. */ 
  1935. public function wp_deleteTerm( $args ) { 
  1936. if ( ! $this->minimum_args( $args, 5 ) ) 
  1937. return $this->error; 
  1938.  
  1939. $this->escape( $args ); 
  1940.  
  1941. $username = $args[1]; 
  1942. $password = $args[2]; 
  1943. $taxonomy = $args[3]; 
  1944. $term_id = (int) $args[4]; 
  1945.  
  1946. if ( ! $user = $this->login( $username, $password ) ) 
  1947. return $this->error; 
  1948.  
  1949. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1950. do_action( 'xmlrpc_call', 'wp.deleteTerm' ); 
  1951.  
  1952. if ( ! taxonomy_exists( $taxonomy ) ) 
  1953. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  1954.  
  1955. $taxonomy = get_taxonomy( $taxonomy ); 
  1956. $term = get_term( $term_id, $taxonomy->name ); 
  1957.  
  1958. if ( is_wp_error( $term ) ) 
  1959. return new IXR_Error( 500, $term->get_error_message() ); 
  1960.  
  1961. if ( ! $term ) 
  1962. return new IXR_Error( 404, __( 'Invalid term ID.' ) ); 
  1963.  
  1964. if ( ! current_user_can( 'delete_term', $term_id ) ) { 
  1965. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this term.' ) ); 
  1966.  
  1967. $result = wp_delete_term( $term_id, $taxonomy->name ); 
  1968.  
  1969. if ( is_wp_error( $result ) ) 
  1970. return new IXR_Error( 500, $term->get_error_message() ); 
  1971.  
  1972. if ( ! $result ) 
  1973. return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) ); 
  1974.  
  1975. return $result; 
  1976.  
  1977. /** 
  1978. * Retrieve a term. 
  1979. * 
  1980. * @since 3.4.0 
  1981. * 
  1982. * @see get_term() 
  1983. * 
  1984. * @param array $args { 
  1985. * Method arguments. Note: arguments must be ordered as documented. 
  1986. * 
  1987. * @type int $blog_id Blog ID (unused). 
  1988. * @type string $username Username. 
  1989. * @type string $password Password. 
  1990. * @type string $taxnomy Taxonomy name. 
  1991. * @type string $term_id Term ID. 
  1992. * } 
  1993. * @return array|IXR_Error IXR_Error on failure, array on success, containing: 
  1994. * - 'term_id' 
  1995. * - 'name' 
  1996. * - 'slug' 
  1997. * - 'term_group' 
  1998. * - 'term_taxonomy_id' 
  1999. * - 'taxonomy' 
  2000. * - 'description' 
  2001. * - 'parent' 
  2002. * - 'count' 
  2003. */ 
  2004. public function wp_getTerm( $args ) { 
  2005. if ( ! $this->minimum_args( $args, 5 ) ) 
  2006. return $this->error; 
  2007.  
  2008. $this->escape( $args ); 
  2009.  
  2010. $username = $args[1]; 
  2011. $password = $args[2]; 
  2012. $taxonomy = $args[3]; 
  2013. $term_id = (int) $args[4]; 
  2014.  
  2015. if ( ! $user = $this->login( $username, $password ) ) 
  2016. return $this->error; 
  2017.  
  2018. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2019. do_action( 'xmlrpc_call', 'wp.getTerm' ); 
  2020.  
  2021. if ( ! taxonomy_exists( $taxonomy ) ) 
  2022. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  2023.  
  2024. $taxonomy = get_taxonomy( $taxonomy ); 
  2025.  
  2026. $term = get_term( $term_id , $taxonomy->name, ARRAY_A ); 
  2027.  
  2028. if ( is_wp_error( $term ) ) 
  2029. return new IXR_Error( 500, $term->get_error_message() ); 
  2030.  
  2031. if ( ! $term ) 
  2032. return new IXR_Error( 404, __( 'Invalid term ID.' ) ); 
  2033.  
  2034. if ( ! current_user_can( 'assign_term', $term_id ) ) { 
  2035. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign this term.' ) ); 
  2036.  
  2037. return $this->_prepare_term( $term ); 
  2038.  
  2039. /** 
  2040. * Retrieve all terms for a taxonomy. 
  2041. * 
  2042. * @since 3.4.0 
  2043. * 
  2044. * The optional $filter parameter modifies the query used to retrieve terms. 
  2045. * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'. 
  2046. * 
  2047. * @see get_terms() 
  2048. * 
  2049. * @param array $args { 
  2050. * Method arguments. Note: arguments must be ordered as documented. 
  2051. * 
  2052. * @type int $blog_id Blog ID (unused). 
  2053. * @type string $username Username. 
  2054. * @type string $password Password. 
  2055. * @type string $taxnomy Taxonomy name. 
  2056. * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'number',  
  2057. * 'offset', 'orderby', 'order', 'hide_empty', and 'search'. Default empty array. 
  2058. * } 
  2059. * @return array|IXR_Error An associative array of terms data on success, IXR_Error instance otherwise. 
  2060. */ 
  2061. public function wp_getTerms( $args ) { 
  2062. if ( ! $this->minimum_args( $args, 4 ) ) 
  2063. return $this->error; 
  2064.  
  2065. $this->escape( $args ); 
  2066.  
  2067. $username = $args[1]; 
  2068. $password = $args[2]; 
  2069. $taxonomy = $args[3]; 
  2070. $filter = isset( $args[4] ) ? $args[4] : array(); 
  2071.  
  2072. if ( ! $user = $this->login( $username, $password ) ) 
  2073. return $this->error; 
  2074.  
  2075. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2076. do_action( 'xmlrpc_call', 'wp.getTerms' ); 
  2077.  
  2078. if ( ! taxonomy_exists( $taxonomy ) ) 
  2079. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  2080.  
  2081. $taxonomy = get_taxonomy( $taxonomy ); 
  2082.  
  2083. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) 
  2084. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); 
  2085.  
  2086. $query = array(); 
  2087.  
  2088. if ( isset( $filter['number'] ) ) 
  2089. $query['number'] = absint( $filter['number'] ); 
  2090.  
  2091. if ( isset( $filter['offset'] ) ) 
  2092. $query['offset'] = absint( $filter['offset'] ); 
  2093.  
  2094. if ( isset( $filter['orderby'] ) ) { 
  2095. $query['orderby'] = $filter['orderby']; 
  2096.  
  2097. if ( isset( $filter['order'] ) ) 
  2098. $query['order'] = $filter['order']; 
  2099.  
  2100. if ( isset( $filter['hide_empty'] ) ) 
  2101. $query['hide_empty'] = $filter['hide_empty']; 
  2102. else 
  2103. $query['get'] = 'all'; 
  2104.  
  2105. if ( isset( $filter['search'] ) ) 
  2106. $query['search'] = $filter['search']; 
  2107.  
  2108. $terms = get_terms( $taxonomy->name, $query ); 
  2109.  
  2110. if ( is_wp_error( $terms ) ) 
  2111. return new IXR_Error( 500, $terms->get_error_message() ); 
  2112.  
  2113. $struct = array(); 
  2114.  
  2115. foreach ( $terms as $term ) { 
  2116. $struct[] = $this->_prepare_term( $term ); 
  2117.  
  2118. return $struct; 
  2119.  
  2120. /** 
  2121. * Retrieve a taxonomy. 
  2122. * 
  2123. * @since 3.4.0 
  2124. * 
  2125. * @see get_taxonomy() 
  2126. * 
  2127. * @param array $args { 
  2128. * Method arguments. Note: arguments must be ordered as documented. 
  2129. * 
  2130. * @type int $blog_id Blog ID (unused). 
  2131. * @type string $username Username. 
  2132. * @type string $password Password. 
  2133. * @type string $taxnomy Taxonomy name. 
  2134. * @type array $fields Optional. Array of taxonomy fields to limit to in the return. 
  2135. * Accepts 'labels', 'cap', 'menu', and 'object_type'. 
  2136. * Default empty array. 
  2137. * } 
  2138. * @return array|IXR_Error An array of taxonomy data on success, IXR_Error instance otherwise. 
  2139. */ 
  2140. public function wp_getTaxonomy( $args ) { 
  2141. if ( ! $this->minimum_args( $args, 4 ) ) 
  2142. return $this->error; 
  2143.  
  2144. $this->escape( $args ); 
  2145.  
  2146. $username = $args[1]; 
  2147. $password = $args[2]; 
  2148. $taxonomy = $args[3]; 
  2149.  
  2150. if ( isset( $args[4] ) ) { 
  2151. $fields = $args[4]; 
  2152. } else { 
  2153. /** 
  2154. * Filters the taxonomy query fields used by the given XML-RPC method. 
  2155. * 
  2156. * @since 3.4.0 
  2157. * 
  2158. * @param array $fields An array of taxonomy fields to retrieve. 
  2159. * @param string $method The method name. 
  2160. */ 
  2161. $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' ); 
  2162.  
  2163. if ( ! $user = $this->login( $username, $password ) ) 
  2164. return $this->error; 
  2165.  
  2166. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2167. do_action( 'xmlrpc_call', 'wp.getTaxonomy' ); 
  2168.  
  2169. if ( ! taxonomy_exists( $taxonomy ) ) 
  2170. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  2171.  
  2172. $taxonomy = get_taxonomy( $taxonomy ); 
  2173.  
  2174. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) 
  2175. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); 
  2176.  
  2177. return $this->_prepare_taxonomy( $taxonomy, $fields ); 
  2178.  
  2179. /** 
  2180. * Retrieve all taxonomies. 
  2181. * 
  2182. * @since 3.4.0 
  2183. * 
  2184. * @see get_taxonomies() 
  2185. * 
  2186. * @param array $args { 
  2187. * Method arguments. Note: arguments must be ordered as documented. 
  2188. * 
  2189. * @type int $blog_id Blog ID (unused). 
  2190. * @type string $username Username. 
  2191. * @type string $password Password. 
  2192. * @type array $filter Optional. An array of arguments for retrieving taxonomies. 
  2193. * @type array $fields Optional. The subset of taxonomy fields to return. 
  2194. * } 
  2195. * @return array|IXR_Error An associative array of taxonomy data with returned fields determined 
  2196. * by `$fields`, or an IXR_Error instance on failure. 
  2197. */ 
  2198. public function wp_getTaxonomies( $args ) { 
  2199. if ( ! $this->minimum_args( $args, 3 ) ) 
  2200. return $this->error; 
  2201.  
  2202. $this->escape( $args ); 
  2203.  
  2204. $username = $args[1]; 
  2205. $password = $args[2]; 
  2206. $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); 
  2207.  
  2208. if ( isset( $args[4] ) ) { 
  2209. $fields = $args[4]; 
  2210. } else { 
  2211. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2212. $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' ); 
  2213.  
  2214. if ( ! $user = $this->login( $username, $password ) ) 
  2215. return $this->error; 
  2216.  
  2217. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2218. do_action( 'xmlrpc_call', 'wp.getTaxonomies' ); 
  2219.  
  2220. $taxonomies = get_taxonomies( $filter, 'objects' ); 
  2221.  
  2222. // holds all the taxonomy data 
  2223. $struct = array(); 
  2224.  
  2225. foreach ( $taxonomies as $taxonomy ) { 
  2226. // capability check for post_types 
  2227. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) 
  2228. continue; 
  2229.  
  2230. $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields ); 
  2231.  
  2232. return $struct; 
  2233.  
  2234. /** 
  2235. * Retrieve a user. 
  2236. * 
  2237. * The optional $fields parameter specifies what fields will be included 
  2238. * in the response array. This should be a list of field names. 'user_id' will 
  2239. * always be included in the response regardless of the value of $fields. 
  2240. * 
  2241. * Instead of, or in addition to, individual field names, conceptual group 
  2242. * names can be used to specify multiple fields. The available conceptual 
  2243. * groups are 'basic' and 'all'. 
  2244. * 
  2245. * @uses get_userdata() 
  2246. * 
  2247. * @param array $args { 
  2248. * Method arguments. Note: arguments must be ordered as documented. 
  2249. * 
  2250. * @type int $blog_id (unused) 
  2251. * @type string $username 
  2252. * @type string $password 
  2253. * @type int $user_id 
  2254. * @type array $fields (optional) 
  2255. * } 
  2256. * @return array|IXR_Error Array contains (based on $fields parameter): 
  2257. * - 'user_id' 
  2258. * - 'username' 
  2259. * - 'first_name' 
  2260. * - 'last_name' 
  2261. * - 'registered' 
  2262. * - 'bio' 
  2263. * - 'email' 
  2264. * - 'nickname' 
  2265. * - 'nicename' 
  2266. * - 'url' 
  2267. * - 'display_name' 
  2268. * - 'roles' 
  2269. */ 
  2270. public function wp_getUser( $args ) { 
  2271. if ( ! $this->minimum_args( $args, 4 ) ) 
  2272. return $this->error; 
  2273.  
  2274. $this->escape( $args ); 
  2275.  
  2276. $username = $args[1]; 
  2277. $password = $args[2]; 
  2278. $user_id = (int) $args[3]; 
  2279.  
  2280. if ( isset( $args[4] ) ) { 
  2281. $fields = $args[4]; 
  2282. } else { 
  2283. /** 
  2284. * Filters the default user query fields used by the given XML-RPC method. 
  2285. * 
  2286. * @since 3.5.0 
  2287. * 
  2288. * @param array $fields User query fields for given method. Default 'all'. 
  2289. * @param string $method The method name. 
  2290. */ 
  2291. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' ); 
  2292.  
  2293. if ( ! $user = $this->login( $username, $password ) ) 
  2294. return $this->error; 
  2295.  
  2296. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2297. do_action( 'xmlrpc_call', 'wp.getUser' ); 
  2298.  
  2299. if ( ! current_user_can( 'edit_user', $user_id ) ) 
  2300. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this user.' ) ); 
  2301.  
  2302. $user_data = get_userdata( $user_id ); 
  2303.  
  2304. if ( ! $user_data ) 
  2305. return new IXR_Error( 404, __( 'Invalid user ID.' ) ); 
  2306.  
  2307. return $this->_prepare_user( $user_data, $fields ); 
  2308.  
  2309. /** 
  2310. * Retrieve users. 
  2311. * 
  2312. * The optional $filter parameter modifies the query used to retrieve users. 
  2313. * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role',  
  2314. * 'who', 'orderby', and 'order'. 
  2315. * 
  2316. * The optional $fields parameter specifies what fields will be included 
  2317. * in the response array. 
  2318. * 
  2319. * @uses get_users() 
  2320. * @see wp_getUser() for more on $fields and return values 
  2321. * 
  2322. * @param array $args { 
  2323. * Method arguments. Note: arguments must be ordered as documented. 
  2324. * 
  2325. * @type int $blog_id (unused) 
  2326. * @type string $username 
  2327. * @type string $password 
  2328. * @type array $filter (optional) 
  2329. * @type array $fields (optional) 
  2330. * } 
  2331. * @return array|IXR_Error users data 
  2332. */ 
  2333. public function wp_getUsers( $args ) { 
  2334. if ( ! $this->minimum_args( $args, 3 ) ) 
  2335. return $this->error; 
  2336.  
  2337. $this->escape( $args ); 
  2338.  
  2339. $username = $args[1]; 
  2340. $password = $args[2]; 
  2341. $filter = isset( $args[3] ) ? $args[3] : array(); 
  2342.  
  2343. if ( isset( $args[4] ) ) { 
  2344. $fields = $args[4]; 
  2345. } else { 
  2346. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2347. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' ); 
  2348.  
  2349. if ( ! $user = $this->login( $username, $password ) ) 
  2350. return $this->error; 
  2351.  
  2352. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2353. do_action( 'xmlrpc_call', 'wp.getUsers' ); 
  2354.  
  2355. if ( ! current_user_can( 'list_users' ) ) 
  2356. return new IXR_Error( 401, __( 'Sorry, you are not allowed to list users.' ) ); 
  2357.  
  2358. $query = array( 'fields' => 'all_with_meta' ); 
  2359.  
  2360. $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50; 
  2361. $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0; 
  2362.  
  2363. if ( isset( $filter['orderby'] ) ) { 
  2364. $query['orderby'] = $filter['orderby']; 
  2365.  
  2366. if ( isset( $filter['order'] ) ) 
  2367. $query['order'] = $filter['order']; 
  2368.  
  2369. if ( isset( $filter['role'] ) ) { 
  2370. if ( get_role( $filter['role'] ) === null ) 
  2371. return new IXR_Error( 403, __( 'Invalid role.' ) ); 
  2372.  
  2373. $query['role'] = $filter['role']; 
  2374.  
  2375. if ( isset( $filter['who'] ) ) { 
  2376. $query['who'] = $filter['who']; 
  2377.  
  2378. $users = get_users( $query ); 
  2379.  
  2380. $_users = array(); 
  2381. foreach ( $users as $user_data ) { 
  2382. if ( current_user_can( 'edit_user', $user_data->ID ) ) 
  2383. $_users[] = $this->_prepare_user( $user_data, $fields ); 
  2384. return $_users; 
  2385.  
  2386. /** 
  2387. * Retrieve information about the requesting user. 
  2388. * 
  2389. * @uses get_userdata() 
  2390. * 
  2391. * @param array $args { 
  2392. * Method arguments. Note: arguments must be ordered as documented. 
  2393. * 
  2394. * @type int $blog_id (unused) 
  2395. * @type string $username 
  2396. * @type string $password 
  2397. * @type array $fields (optional) 
  2398. * } 
  2399. * @return array|IXR_Error (@see wp_getUser) 
  2400. */ 
  2401. public function wp_getProfile( $args ) { 
  2402. if ( ! $this->minimum_args( $args, 3 ) ) 
  2403. return $this->error; 
  2404.  
  2405. $this->escape( $args ); 
  2406.  
  2407. $username = $args[1]; 
  2408. $password = $args[2]; 
  2409.  
  2410. if ( isset( $args[3] ) ) { 
  2411. $fields = $args[3]; 
  2412. } else { 
  2413. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2414. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' ); 
  2415.  
  2416. if ( ! $user = $this->login( $username, $password ) ) 
  2417. return $this->error; 
  2418.  
  2419. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2420. do_action( 'xmlrpc_call', 'wp.getProfile' ); 
  2421.  
  2422. if ( ! current_user_can( 'edit_user', $user->ID ) ) 
  2423. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); 
  2424.  
  2425. $user_data = get_userdata( $user->ID ); 
  2426.  
  2427. return $this->_prepare_user( $user_data, $fields ); 
  2428.  
  2429. /** 
  2430. * Edit user's profile. 
  2431. * 
  2432. * @uses wp_update_user() 
  2433. * 
  2434. * @param array $args { 
  2435. * Method arguments. Note: arguments must be ordered as documented. 
  2436. * 
  2437. * @type int $blog_id (unused) 
  2438. * @type string $username 
  2439. * @type string $password 
  2440. * @type array $content_struct It can optionally contain: 
  2441. * - 'first_name' 
  2442. * - 'last_name' 
  2443. * - 'website' 
  2444. * - 'display_name' 
  2445. * - 'nickname' 
  2446. * - 'nicename' 
  2447. * - 'bio' 
  2448. * } 
  2449. * @return true|IXR_Error True, on success. 
  2450. */ 
  2451. public function wp_editProfile( $args ) { 
  2452. if ( ! $this->minimum_args( $args, 4 ) ) 
  2453. return $this->error; 
  2454.  
  2455. $this->escape( $args ); 
  2456.  
  2457. $username = $args[1]; 
  2458. $password = $args[2]; 
  2459. $content_struct = $args[3]; 
  2460.  
  2461. if ( ! $user = $this->login( $username, $password ) ) 
  2462. return $this->error; 
  2463.  
  2464. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2465. do_action( 'xmlrpc_call', 'wp.editProfile' ); 
  2466.  
  2467. if ( ! current_user_can( 'edit_user', $user->ID ) ) 
  2468. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); 
  2469.  
  2470. // holds data of the user 
  2471. $user_data = array(); 
  2472. $user_data['ID'] = $user->ID; 
  2473.  
  2474. // only set the user details if it was given 
  2475. if ( isset( $content_struct['first_name'] ) ) 
  2476. $user_data['first_name'] = $content_struct['first_name']; 
  2477.  
  2478. if ( isset( $content_struct['last_name'] ) ) 
  2479. $user_data['last_name'] = $content_struct['last_name']; 
  2480.  
  2481. if ( isset( $content_struct['url'] ) ) 
  2482. $user_data['user_url'] = $content_struct['url']; 
  2483.  
  2484. if ( isset( $content_struct['display_name'] ) ) 
  2485. $user_data['display_name'] = $content_struct['display_name']; 
  2486.  
  2487. if ( isset( $content_struct['nickname'] ) ) 
  2488. $user_data['nickname'] = $content_struct['nickname']; 
  2489.  
  2490. if ( isset( $content_struct['nicename'] ) ) 
  2491. $user_data['user_nicename'] = $content_struct['nicename']; 
  2492.  
  2493. if ( isset( $content_struct['bio'] ) ) 
  2494. $user_data['description'] = $content_struct['bio']; 
  2495.  
  2496. $result = wp_update_user( $user_data ); 
  2497.  
  2498. if ( is_wp_error( $result ) ) 
  2499. return new IXR_Error( 500, $result->get_error_message() ); 
  2500.  
  2501. if ( ! $result ) 
  2502. return new IXR_Error( 500, __( 'Sorry, the user cannot be updated.' ) ); 
  2503.  
  2504. return true; 
  2505.  
  2506. /** 
  2507. * Retrieve page. 
  2508. * 
  2509. * @since 2.2.0 
  2510. * 
  2511. * @param array $args { 
  2512. * Method arguments. Note: arguments must be ordered as documented. 
  2513. * 
  2514. * @type int $blog_id (unused) 
  2515. * @type int $page_id 
  2516. * @type string $username 
  2517. * @type string $password 
  2518. * } 
  2519. * @return array|IXR_Error 
  2520. */ 
  2521. public function wp_getPage( $args ) { 
  2522. $this->escape( $args ); 
  2523.  
  2524. $page_id = (int) $args[1]; 
  2525. $username = $args[2]; 
  2526. $password = $args[3]; 
  2527.  
  2528. if ( !$user = $this->login($username, $password) ) { 
  2529. return $this->error; 
  2530.  
  2531. $page = get_post($page_id); 
  2532. if ( ! $page ) 
  2533. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  2534.  
  2535. if ( !current_user_can( 'edit_page', $page_id ) ) 
  2536. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); 
  2537.  
  2538. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2539. do_action( 'xmlrpc_call', 'wp.getPage' ); 
  2540.  
  2541. // If we found the page then format the data. 
  2542. if ( $page->ID && ($page->post_type == 'page') ) { 
  2543. return $this->_prepare_page( $page ); 
  2544. // If the page doesn't exist indicate that. 
  2545. else { 
  2546. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); 
  2547.  
  2548. /** 
  2549. * Retrieve Pages. 
  2550. * 
  2551. * @since 2.2.0 
  2552. * 
  2553. * @param array $args { 
  2554. * Method arguments. Note: arguments must be ordered as documented. 
  2555. * 
  2556. * @type int $blog_id (unused) 
  2557. * @type string $username 
  2558. * @type string $password 
  2559. * @type int $num_pages 
  2560. * } 
  2561. * @return array|IXR_Error 
  2562. */ 
  2563. public function wp_getPages( $args ) { 
  2564. $this->escape( $args ); 
  2565.  
  2566. $username = $args[1]; 
  2567. $password = $args[2]; 
  2568. $num_pages = isset($args[3]) ? (int) $args[3] : 10; 
  2569.  
  2570. if ( !$user = $this->login($username, $password) ) 
  2571. return $this->error; 
  2572.  
  2573. if ( !current_user_can( 'edit_pages' ) ) 
  2574. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); 
  2575.  
  2576. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2577. do_action( 'xmlrpc_call', 'wp.getPages' ); 
  2578.  
  2579. $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) ); 
  2580. $num_pages = count($pages); 
  2581.  
  2582. // If we have pages, put together their info. 
  2583. if ( $num_pages >= 1 ) { 
  2584. $pages_struct = array(); 
  2585.  
  2586. foreach ($pages as $page) { 
  2587. if ( current_user_can( 'edit_page', $page->ID ) ) 
  2588. $pages_struct[] = $this->_prepare_page( $page ); 
  2589.  
  2590. return $pages_struct; 
  2591.  
  2592. return array(); 
  2593.  
  2594. /** 
  2595. * Create new page. 
  2596. * 
  2597. * @since 2.2.0 
  2598. * 
  2599. * @see wp_xmlrpc_server::mw_newPost() 
  2600. * 
  2601. * @param array $args { 
  2602. * Method arguments. Note: arguments must be ordered as documented. 
  2603. * 
  2604. * @type int $blog_id (unused) 
  2605. * @type string $username 
  2606. * @type string $password 
  2607. * @type array $content_struct 
  2608. * } 
  2609. * @return int|IXR_Error 
  2610. */ 
  2611. public function wp_newPage( $args ) { 
  2612. // Items not escaped here will be escaped in newPost. 
  2613. $username = $this->escape( $args[1] ); 
  2614. $password = $this->escape( $args[2] ); 
  2615.  
  2616. if ( !$user = $this->login($username, $password) ) 
  2617. return $this->error; 
  2618.  
  2619. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2620. do_action( 'xmlrpc_call', 'wp.newPage' ); 
  2621.  
  2622. // Mark this as content for a page. 
  2623. $args[3]["post_type"] = 'page'; 
  2624.  
  2625. // Let mw_newPost do all of the heavy lifting. 
  2626. return $this->mw_newPost( $args ); 
  2627.  
  2628. /** 
  2629. * Delete page. 
  2630. * 
  2631. * @since 2.2.0 
  2632. * 
  2633. * @param array $args { 
  2634. * Method arguments. Note: arguments must be ordered as documented. 
  2635. * 
  2636. * @type int $blog_id (unused) 
  2637. * @type string $username 
  2638. * @type string $password 
  2639. * @type int $page_id 
  2640. * } 
  2641. * @return true|IXR_Error True, if success. 
  2642. */ 
  2643. public function wp_deletePage( $args ) { 
  2644. $this->escape( $args ); 
  2645.  
  2646. $username = $args[1]; 
  2647. $password = $args[2]; 
  2648. $page_id = (int) $args[3]; 
  2649.  
  2650. if ( !$user = $this->login($username, $password) ) 
  2651. return $this->error; 
  2652.  
  2653. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2654. do_action( 'xmlrpc_call', 'wp.deletePage' ); 
  2655.  
  2656. // Get the current page based on the page_id and 
  2657. // make sure it is a page and not a post. 
  2658. $actual_page = get_post($page_id, ARRAY_A); 
  2659. if ( !$actual_page || ($actual_page['post_type'] != 'page') ) 
  2660. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); 
  2661.  
  2662. // Make sure the user can delete pages. 
  2663. if ( !current_user_can('delete_page', $page_id) ) 
  2664. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this page.' ) ); 
  2665.  
  2666. // Attempt to delete the page. 
  2667. $result = wp_delete_post($page_id); 
  2668. if ( !$result ) 
  2669. return new IXR_Error( 500, __( 'Failed to delete the page.' ) ); 
  2670.  
  2671. /** 
  2672. * Fires after a page has been successfully deleted via XML-RPC. 
  2673. * 
  2674. * @since 3.4.0 
  2675. * 
  2676. * @param int $page_id ID of the deleted page. 
  2677. * @param array $args An array of arguments to delete the page. 
  2678. */ 
  2679. do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args ); 
  2680.  
  2681. return true; 
  2682.  
  2683. /** 
  2684. * Edit page. 
  2685. * 
  2686. * @since 2.2.0 
  2687. * 
  2688. * @param array $args { 
  2689. * Method arguments. Note: arguments must be ordered as documented. 
  2690. * 
  2691. * @type int $blog_id (unused) 
  2692. * @type int $page_id 
  2693. * @type string $username 
  2694. * @type string $password 
  2695. * @type string $content 
  2696. * @type string $publish 
  2697. * } 
  2698. * @return array|IXR_Error 
  2699. */ 
  2700. public function wp_editPage( $args ) { 
  2701. // Items will be escaped in mw_editPost. 
  2702. $page_id = (int) $args[1]; 
  2703. $username = $args[2]; 
  2704. $password = $args[3]; 
  2705. $content = $args[4]; 
  2706. $publish = $args[5]; 
  2707.  
  2708. $escaped_username = $this->escape( $username ); 
  2709. $escaped_password = $this->escape( $password ); 
  2710.  
  2711. if ( !$user = $this->login( $escaped_username, $escaped_password ) ) { 
  2712. return $this->error; 
  2713.  
  2714. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2715. do_action( 'xmlrpc_call', 'wp.editPage' ); 
  2716.  
  2717. // Get the page data and make sure it is a page. 
  2718. $actual_page = get_post($page_id, ARRAY_A); 
  2719. if ( !$actual_page || ($actual_page['post_type'] != 'page') ) 
  2720. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); 
  2721.  
  2722. // Make sure the user is allowed to edit pages. 
  2723. if ( !current_user_can('edit_page', $page_id) ) 
  2724. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); 
  2725.  
  2726. // Mark this as content for a page. 
  2727. $content['post_type'] = 'page'; 
  2728.  
  2729. // Arrange args in the way mw_editPost understands. 
  2730. $args = array( 
  2731. $page_id,  
  2732. $username,  
  2733. $password,  
  2734. $content,  
  2735. $publish 
  2736. ); 
  2737.  
  2738. // Let mw_editPost do all of the heavy lifting. 
  2739. return $this->mw_editPost( $args ); 
  2740.  
  2741. /** 
  2742. * Retrieve page list. 
  2743. * 
  2744. * @since 2.2.0 
  2745. * 
  2746. * @global wpdb $wpdb WordPress database abstraction object. 
  2747. * 
  2748. * @param array $args { 
  2749. * Method arguments. Note: arguments must be ordered as documented. 
  2750. * 
  2751. * @type int $blog_id (unused) 
  2752. * @type string $username 
  2753. * @type string $password 
  2754. * } 
  2755. * @return array|IXR_Error 
  2756. */ 
  2757. public function wp_getPageList( $args ) { 
  2758. global $wpdb; 
  2759.  
  2760. $this->escape( $args ); 
  2761.  
  2762. $username = $args[1]; 
  2763. $password = $args[2]; 
  2764.  
  2765. if ( !$user = $this->login($username, $password) ) 
  2766. return $this->error; 
  2767.  
  2768. if ( !current_user_can( 'edit_pages' ) ) 
  2769. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); 
  2770.  
  2771. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2772. do_action( 'xmlrpc_call', 'wp.getPageList' ); 
  2773.  
  2774. // Get list of pages ids and titles 
  2775. $page_list = $wpdb->get_results(" 
  2776. SELECT ID page_id,  
  2777. post_title page_title,  
  2778. post_parent page_parent_id,  
  2779. post_date_gmt,  
  2780. post_date,  
  2781. post_status 
  2782. FROM {$wpdb->posts} 
  2783. WHERE post_type = 'page' 
  2784. ORDER BY ID 
  2785. "); 
  2786.  
  2787. // The date needs to be formatted properly. 
  2788. $num_pages = count($page_list); 
  2789. for ( $i = 0; $i < $num_pages; $i++ ) { 
  2790. $page_list[$i]->dateCreated = $this->_convert_date( $page_list[$i]->post_date ); 
  2791. $page_list[$i]->date_created_gmt = $this->_convert_date_gmt( $page_list[$i]->post_date_gmt, $page_list[$i]->post_date ); 
  2792.  
  2793. unset($page_list[$i]->post_date_gmt); 
  2794. unset($page_list[$i]->post_date); 
  2795. unset($page_list[$i]->post_status); 
  2796.  
  2797. return $page_list; 
  2798.  
  2799. /** 
  2800. * Retrieve authors list. 
  2801. * 
  2802. * @since 2.2.0 
  2803. * 
  2804. * @param array $args { 
  2805. * Method arguments. Note: arguments must be ordered as documented. 
  2806. * 
  2807. * @type int $blog_id (unused) 
  2808. * @type string $username 
  2809. * @type string $password 
  2810. * } 
  2811. * @return array|IXR_Error 
  2812. */ 
  2813. public function wp_getAuthors( $args ) { 
  2814. $this->escape( $args ); 
  2815.  
  2816. $username = $args[1]; 
  2817. $password = $args[2]; 
  2818.  
  2819. if ( !$user = $this->login($username, $password) ) 
  2820. return $this->error; 
  2821.  
  2822. if ( !current_user_can('edit_posts') ) 
  2823. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  2824.  
  2825. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2826. do_action( 'xmlrpc_call', 'wp.getAuthors' ); 
  2827.  
  2828. $authors = array(); 
  2829. foreach ( get_users( array( 'fields' => array('ID', 'user_login', 'display_name') ) ) as $user ) { 
  2830. $authors[] = array( 
  2831. 'user_id' => $user->ID,  
  2832. 'user_login' => $user->user_login,  
  2833. 'display_name' => $user->display_name 
  2834. ); 
  2835.  
  2836. return $authors; 
  2837.  
  2838. /** 
  2839. * Get list of all tags 
  2840. * 
  2841. * @since 2.7.0 
  2842. * 
  2843. * @param array $args { 
  2844. * Method arguments. Note: arguments must be ordered as documented. 
  2845. * 
  2846. * @type int $blog_id (unused) 
  2847. * @type string $username 
  2848. * @type string $password 
  2849. * } 
  2850. * @return array|IXR_Error 
  2851. */ 
  2852. public function wp_getTags( $args ) { 
  2853. $this->escape( $args ); 
  2854.  
  2855. $username = $args[1]; 
  2856. $password = $args[2]; 
  2857.  
  2858. if ( !$user = $this->login($username, $password) ) 
  2859. return $this->error; 
  2860.  
  2861. if ( !current_user_can( 'edit_posts' ) ) 
  2862. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) ); 
  2863.  
  2864. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2865. do_action( 'xmlrpc_call', 'wp.getKeywords' ); 
  2866.  
  2867. $tags = array(); 
  2868.  
  2869. if ( $all_tags = get_tags() ) { 
  2870. foreach ( (array) $all_tags as $tag ) { 
  2871. $struct = array(); 
  2872. $struct['tag_id'] = $tag->term_id; 
  2873. $struct['name'] = $tag->name; 
  2874. $struct['count'] = $tag->count; 
  2875. $struct['slug'] = $tag->slug; 
  2876. $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) ); 
  2877. $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) ); 
  2878.  
  2879. $tags[] = $struct; 
  2880.  
  2881. return $tags; 
  2882.  
  2883. /** 
  2884. * Create new category. 
  2885. * 
  2886. * @since 2.2.0 
  2887. * 
  2888. * @param array $args { 
  2889. * Method arguments. Note: arguments must be ordered as documented. 
  2890. * 
  2891. * @type int $blog_id (unused) 
  2892. * @type string $username 
  2893. * @type string $password 
  2894. * @type array $category 
  2895. * } 
  2896. * @return int|IXR_Error Category ID. 
  2897. */ 
  2898. public function wp_newCategory( $args ) { 
  2899. $this->escape( $args ); 
  2900.  
  2901. $username = $args[1]; 
  2902. $password = $args[2]; 
  2903. $category = $args[3]; 
  2904.  
  2905. if ( !$user = $this->login($username, $password) ) 
  2906. return $this->error; 
  2907.  
  2908. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2909. do_action( 'xmlrpc_call', 'wp.newCategory' ); 
  2910.  
  2911. // Make sure the user is allowed to add a category. 
  2912. if ( !current_user_can('manage_categories') ) 
  2913. return new IXR_Error(401, __('Sorry, you are not allowed to add a category.')); 
  2914.  
  2915. // If no slug was provided make it empty so that 
  2916. // WordPress will generate one. 
  2917. if ( empty($category['slug']) ) 
  2918. $category['slug'] = ''; 
  2919.  
  2920. // If no parent_id was provided make it empty 
  2921. // so that it will be a top level page (no parent). 
  2922. if ( !isset($category['parent_id']) ) 
  2923. $category['parent_id'] = ''; 
  2924.  
  2925. // If no description was provided make it empty. 
  2926. if ( empty($category["description"]) ) 
  2927. $category["description"] = ""; 
  2928.  
  2929. $new_category = array( 
  2930. 'cat_name' => $category['name'],  
  2931. 'category_nicename' => $category['slug'],  
  2932. 'category_parent' => $category['parent_id'],  
  2933. 'category_description' => $category['description'] 
  2934. ); 
  2935.  
  2936. $cat_id = wp_insert_category($new_category, true); 
  2937. if ( is_wp_error( $cat_id ) ) { 
  2938. if ( 'term_exists' == $cat_id->get_error_code() ) 
  2939. return (int) $cat_id->get_error_data(); 
  2940. else 
  2941. return new IXR_Error(500, __('Sorry, the new category failed.')); 
  2942. } elseif ( ! $cat_id ) { 
  2943. return new IXR_Error(500, __('Sorry, the new category failed.')); 
  2944.  
  2945. /** 
  2946. * Fires after a new category has been successfully created via XML-RPC. 
  2947. * 
  2948. * @since 3.4.0 
  2949. * 
  2950. * @param int $cat_id ID of the new category. 
  2951. * @param array $args An array of new category arguments. 
  2952. */ 
  2953. do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args ); 
  2954.  
  2955. return $cat_id; 
  2956.  
  2957. /** 
  2958. * Remove category. 
  2959. * 
  2960. * @since 2.5.0 
  2961. * 
  2962. * @param array $args { 
  2963. * Method arguments. Note: arguments must be ordered as documented. 
  2964. * 
  2965. * @type int $blog_id (unused) 
  2966. * @type string $username 
  2967. * @type string $password 
  2968. * @type int $category_id 
  2969. * } 
  2970. * @return bool|IXR_Error See wp_delete_term() for return info. 
  2971. */ 
  2972. public function wp_deleteCategory( $args ) { 
  2973. $this->escape( $args ); 
  2974.  
  2975. $username = $args[1]; 
  2976. $password = $args[2]; 
  2977. $category_id = (int) $args[3]; 
  2978.  
  2979. if ( !$user = $this->login($username, $password) ) 
  2980. return $this->error; 
  2981.  
  2982. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2983. do_action( 'xmlrpc_call', 'wp.deleteCategory' ); 
  2984.  
  2985. if ( !current_user_can('manage_categories') ) 
  2986. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete a category.' ) ); 
  2987.  
  2988. $status = wp_delete_term( $category_id, 'category' ); 
  2989.  
  2990. if ( true == $status ) { 
  2991. /** 
  2992. * Fires after a category has been successfully deleted via XML-RPC. 
  2993. * 
  2994. * @since 3.4.0 
  2995. * 
  2996. * @param int $category_id ID of the deleted category. 
  2997. * @param array $args An array of arguments to delete the category. 
  2998. */ 
  2999. do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); 
  3000.  
  3001. return $status; 
  3002.  
  3003. /** 
  3004. * Retrieve category list. 
  3005. * 
  3006. * @since 2.2.0 
  3007. * 
  3008. * @param array $args { 
  3009. * Method arguments. Note: arguments must be ordered as documented. 
  3010. * 
  3011. * @type int $blog_id (unused) 
  3012. * @type string $username 
  3013. * @type string $password 
  3014. * @type array $category 
  3015. * @type int $max_results 
  3016. * } 
  3017. * @return array|IXR_Error 
  3018. */ 
  3019. public function wp_suggestCategories( $args ) { 
  3020. $this->escape( $args ); 
  3021.  
  3022. $username = $args[1]; 
  3023. $password = $args[2]; 
  3024. $category = $args[3]; 
  3025. $max_results = (int) $args[4]; 
  3026.  
  3027. if ( !$user = $this->login($username, $password) ) 
  3028. return $this->error; 
  3029.  
  3030. if ( !current_user_can( 'edit_posts' ) ) 
  3031. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 
  3032.  
  3033. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3034. do_action( 'xmlrpc_call', 'wp.suggestCategories' ); 
  3035.  
  3036. $category_suggestions = array(); 
  3037. $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 
  3038. foreach ( (array) get_categories($args) as $cat ) { 
  3039. $category_suggestions[] = array( 
  3040. 'category_id' => $cat->term_id,  
  3041. 'category_name' => $cat->name 
  3042. ); 
  3043.  
  3044. return $category_suggestions; 
  3045.  
  3046. /** 
  3047. * Retrieve comment. 
  3048. * 
  3049. * @since 2.7.0 
  3050. * 
  3051. * @param array $args { 
  3052. * Method arguments. Note: arguments must be ordered as documented. 
  3053. * 
  3054. * @type int $blog_id (unused) 
  3055. * @type string $username 
  3056. * @type string $password 
  3057. * @type int $comment_id 
  3058. * } 
  3059. * @return array|IXR_Error 
  3060. */ 
  3061. public function wp_getComment($args) { 
  3062. $this->escape($args); 
  3063.  
  3064. $username = $args[1]; 
  3065. $password = $args[2]; 
  3066. $comment_id = (int) $args[3]; 
  3067.  
  3068. if ( ! $user = $this->login( $username, $password ) ) { 
  3069. return $this->error; 
  3070.  
  3071. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3072. do_action( 'xmlrpc_call', 'wp.getComment' ); 
  3073.  
  3074. if ( ! $comment = get_comment( $comment_id ) ) { 
  3075. return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 
  3076.  
  3077. if ( ! current_user_can( 'edit_comment', $comment_id ) ) { 
  3078. return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); 
  3079.  
  3080. return $this->_prepare_comment( $comment ); 
  3081.  
  3082. /** 
  3083. * Retrieve comments. 
  3084. * 
  3085. * Besides the common blog_id (unused), username, and password arguments, it takes a filter 
  3086. * array as last argument. 
  3087. * 
  3088. * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'. 
  3089. * 
  3090. * The defaults are as follows: 
  3091. * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold') 
  3092. * - 'post_id' - Default is ''. The post where the comment is posted. Empty string shows all comments. 
  3093. * - 'number' - Default is 10. Total number of media items to retrieve. 
  3094. * - 'offset' - Default is 0. See WP_Query::query() for more. 
  3095. * 
  3096. * @since 2.7.0 
  3097. * 
  3098. * @param array $args { 
  3099. * Method arguments. Note: arguments must be ordered as documented. 
  3100. * 
  3101. * @type int $blog_id (unused) 
  3102. * @type string $username 
  3103. * @type string $password 
  3104. * @type array $struct 
  3105. * } 
  3106. * @return array|IXR_Error Contains a collection of comments. See wp_xmlrpc_server::wp_getComment() for a description of each item contents 
  3107. */ 
  3108. public function wp_getComments( $args ) { 
  3109. $this->escape( $args ); 
  3110.  
  3111. $username = $args[1]; 
  3112. $password = $args[2]; 
  3113. $struct = isset( $args[3] ) ? $args[3] : array(); 
  3114.  
  3115. if ( ! $user = $this->login( $username, $password ) ) { 
  3116. return $this->error; 
  3117.  
  3118. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3119. do_action( 'xmlrpc_call', 'wp.getComments' ); 
  3120.  
  3121. if ( isset( $struct['status'] ) ) { 
  3122. $status = $struct['status']; 
  3123. } else { 
  3124. $status = ''; 
  3125.  
  3126. if ( ! current_user_can( 'moderate_comments' ) && 'approve' !== $status ) { 
  3127. return new IXR_Error( 401, __( 'Invalid comment status.' ) ); 
  3128.  
  3129. $post_id = ''; 
  3130. if ( isset( $struct['post_id'] ) ) { 
  3131. $post_id = absint( $struct['post_id'] ); 
  3132.  
  3133. $post_type = ''; 
  3134. if ( isset( $struct['post_type'] ) ) { 
  3135. $post_type_object = get_post_type_object( $struct['post_type'] ); 
  3136. if ( ! $post_type_object || ! post_type_supports( $post_type_object->name, 'comments' ) ) { 
  3137. return new IXR_Error( 404, __( 'Invalid post type.' ) ); 
  3138. $post_type = $struct['post_type']; 
  3139.  
  3140. $offset = 0; 
  3141. if ( isset( $struct['offset'] ) ) { 
  3142. $offset = absint( $struct['offset'] ); 
  3143.  
  3144. $number = 10; 
  3145. if ( isset( $struct['number'] ) ) { 
  3146. $number = absint( $struct['number'] ); 
  3147.  
  3148. $comments = get_comments( array( 
  3149. 'status' => $status,  
  3150. 'post_id' => $post_id,  
  3151. 'offset' => $offset,  
  3152. 'number' => $number,  
  3153. 'post_type' => $post_type,  
  3154. ) ); 
  3155.  
  3156. $comments_struct = array(); 
  3157. if ( is_array( $comments ) ) { 
  3158. foreach ( $comments as $comment ) { 
  3159. $comments_struct[] = $this->_prepare_comment( $comment ); 
  3160.  
  3161. return $comments_struct; 
  3162.  
  3163. /** 
  3164. * Delete a comment. 
  3165. * 
  3166. * By default, the comment will be moved to the trash instead of deleted. 
  3167. * See wp_delete_comment() for more information on this behavior. 
  3168. * 
  3169. * @since 2.7.0 
  3170. * 
  3171. * @param array $args { 
  3172. * Method arguments. Note: arguments must be ordered as documented. 
  3173. * 
  3174. * @type int $blog_id (unused) 
  3175. * @type string $username 
  3176. * @type string $password 
  3177. * @type int $comment_ID 
  3178. * } 
  3179. * @return bool|IXR_Error See wp_delete_comment(). 
  3180. */ 
  3181. public function wp_deleteComment( $args ) { 
  3182. $this->escape($args); 
  3183.  
  3184. $username = $args[1]; 
  3185. $password = $args[2]; 
  3186. $comment_ID = (int) $args[3]; 
  3187.  
  3188. if ( ! $user = $this->login( $username, $password ) ) { 
  3189. return $this->error; 
  3190.  
  3191. if ( ! get_comment( $comment_ID ) ) { 
  3192. return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 
  3193.  
  3194. if ( !current_user_can( 'edit_comment', $comment_ID ) ) { 
  3195. return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); 
  3196.  
  3197. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3198. do_action( 'xmlrpc_call', 'wp.deleteComment' ); 
  3199.  
  3200. $status = wp_delete_comment( $comment_ID ); 
  3201.  
  3202. if ( $status ) { 
  3203. /** 
  3204. * Fires after a comment has been successfully deleted via XML-RPC. 
  3205. * 
  3206. * @since 3.4.0 
  3207. * 
  3208. * @param int $comment_ID ID of the deleted comment. 
  3209. * @param array $args An array of arguments to delete the comment. 
  3210. */ 
  3211. do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_ID, $args ); 
  3212.  
  3213. return $status; 
  3214.  
  3215. /** 
  3216. * Edit comment. 
  3217. * 
  3218. * Besides the common blog_id (unused), username, and password arguments, it takes a 
  3219. * comment_id integer and a content_struct array as last argument. 
  3220. * 
  3221. * The allowed keys in the content_struct array are: 
  3222. * - 'author' 
  3223. * - 'author_url' 
  3224. * - 'author_email' 
  3225. * - 'content' 
  3226. * - 'date_created_gmt' 
  3227. * - 'status'. Common statuses are 'approve', 'hold', 'spam'. See get_comment_statuses() for more details 
  3228. * 
  3229. * @since 2.7.0 
  3230. * 
  3231. * @param array $args { 
  3232. * Method arguments. Note: arguments must be ordered as documented. 
  3233. * 
  3234. * @type int $blog_id (unused) 
  3235. * @type string $username 
  3236. * @type string $password 
  3237. * @type int $comment_ID 
  3238. * @type array $content_struct 
  3239. * } 
  3240. * @return true|IXR_Error True, on success. 
  3241. */ 
  3242. public function wp_editComment( $args ) { 
  3243. $this->escape( $args ); 
  3244.  
  3245. $username = $args[1]; 
  3246. $password = $args[2]; 
  3247. $comment_ID = (int) $args[3]; 
  3248. $content_struct = $args[4]; 
  3249.  
  3250. if ( !$user = $this->login( $username, $password ) ) { 
  3251. return $this->error; 
  3252.  
  3253. if ( ! get_comment( $comment_ID ) ) { 
  3254. return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 
  3255.  
  3256. if ( ! current_user_can( 'edit_comment', $comment_ID ) ) { 
  3257. return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); 
  3258.  
  3259. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3260. do_action( 'xmlrpc_call', 'wp.editComment' ); 
  3261.  
  3262. if ( isset($content_struct['status']) ) { 
  3263. $statuses = get_comment_statuses(); 
  3264. $statuses = array_keys($statuses); 
  3265.  
  3266. if ( ! in_array($content_struct['status'], $statuses) ) 
  3267. return new IXR_Error( 401, __( 'Invalid comment status.' ) ); 
  3268. $comment_approved = $content_struct['status']; 
  3269.  
  3270. // Do some timestamp voodoo 
  3271. if ( !empty( $content_struct['date_created_gmt'] ) ) { 
  3272. // We know this is supposed to be GMT, so we're going to slap that Z on there by force 
  3273. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; 
  3274. $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 
  3275. $comment_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); 
  3276.  
  3277. if ( isset($content_struct['content']) ) 
  3278. $comment_content = $content_struct['content']; 
  3279.  
  3280. if ( isset($content_struct['author']) ) 
  3281. $comment_author = $content_struct['author']; 
  3282.  
  3283. if ( isset($content_struct['author_url']) ) 
  3284. $comment_author_url = $content_struct['author_url']; 
  3285.  
  3286. if ( isset($content_struct['author_email']) ) 
  3287. $comment_author_email = $content_struct['author_email']; 
  3288.  
  3289. // We've got all the data -- post it: 
  3290. $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url'); 
  3291.  
  3292. $result = wp_update_comment($comment); 
  3293. if ( is_wp_error( $result ) ) 
  3294. return new IXR_Error(500, $result->get_error_message()); 
  3295.  
  3296. if ( !$result ) 
  3297. return new IXR_Error(500, __('Sorry, the comment could not be edited.')); 
  3298.  
  3299. /** 
  3300. * Fires after a comment has been successfully updated via XML-RPC. 
  3301. * 
  3302. * @since 3.4.0 
  3303. * 
  3304. * @param int $comment_ID ID of the updated comment. 
  3305. * @param array $args An array of arguments to update the comment. 
  3306. */ 
  3307. do_action( 'xmlrpc_call_success_wp_editComment', $comment_ID, $args ); 
  3308.  
  3309. return true; 
  3310.  
  3311. /** 
  3312. * Create new comment. 
  3313. * 
  3314. * @since 2.7.0 
  3315. * 
  3316. * @param array $args { 
  3317. * Method arguments. Note: arguments must be ordered as documented. 
  3318. * 
  3319. * @type int $blog_id (unused) 
  3320. * @type string $username 
  3321. * @type string $password 
  3322. * @type string|int $post 
  3323. * @type array $content_struct 
  3324. * } 
  3325. * @return int|IXR_Error See wp_new_comment(). 
  3326. */ 
  3327. public function wp_newComment($args) { 
  3328. $this->escape($args); 
  3329.  
  3330. $username = $args[1]; 
  3331. $password = $args[2]; 
  3332. $post = $args[3]; 
  3333. $content_struct = $args[4]; 
  3334.  
  3335. /** 
  3336. * Filters whether to allow anonymous comments over XML-RPC. 
  3337. * 
  3338. * @since 2.7.0 
  3339. * 
  3340. * @param bool $allow Whether to allow anonymous commenting via XML-RPC. 
  3341. * Default false. 
  3342. */ 
  3343. $allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false ); 
  3344.  
  3345. $user = $this->login($username, $password); 
  3346.  
  3347. if ( !$user ) { 
  3348. $logged_in = false; 
  3349. if ( $allow_anon && get_option('comment_registration') ) { 
  3350. return new IXR_Error( 403, __( 'You must be registered to comment.' ) ); 
  3351. } elseif ( ! $allow_anon ) { 
  3352. return $this->error; 
  3353. } else { 
  3354. $logged_in = true; 
  3355.  
  3356. if ( is_numeric($post) ) 
  3357. $post_id = absint($post); 
  3358. else 
  3359. $post_id = url_to_postid($post); 
  3360.  
  3361. if ( ! $post_id ) { 
  3362. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3363.  
  3364. if ( ! get_post( $post_id ) ) { 
  3365. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3366.  
  3367. if ( ! comments_open( $post_id ) ) { 
  3368. return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) ); 
  3369.  
  3370. if ( empty( $content_struct['content'] ) ) { 
  3371. return new IXR_Error( 403, __( 'Comment is required.' ) ); 
  3372.  
  3373. $comment = array( 
  3374. 'comment_post_ID' => $post_id,  
  3375. 'comment_content' => $content_struct['content'],  
  3376. ); 
  3377.  
  3378. if ( $logged_in ) { 
  3379. $display_name = $user->display_name; 
  3380. $user_email = $user->user_email; 
  3381. $user_url = $user->user_url; 
  3382.  
  3383. $comment['comment_author'] = $this->escape( $display_name ); 
  3384. $comment['comment_author_email'] = $this->escape( $user_email ); 
  3385. $comment['comment_author_url'] = $this->escape( $user_url ); 
  3386. $comment['user_ID'] = $user->ID; 
  3387. } else { 
  3388. $comment['comment_author'] = ''; 
  3389. if ( isset($content_struct['author']) ) 
  3390. $comment['comment_author'] = $content_struct['author']; 
  3391.  
  3392. $comment['comment_author_email'] = ''; 
  3393. if ( isset($content_struct['author_email']) ) 
  3394. $comment['comment_author_email'] = $content_struct['author_email']; 
  3395.  
  3396. $comment['comment_author_url'] = ''; 
  3397. if ( isset($content_struct['author_url']) ) 
  3398. $comment['comment_author_url'] = $content_struct['author_url']; 
  3399.  
  3400. $comment['user_ID'] = 0; 
  3401.  
  3402. if ( get_option('require_name_email') ) { 
  3403. if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] ) 
  3404. return new IXR_Error( 403, __( 'Comment author name and email are required.' ) ); 
  3405. elseif ( !is_email($comment['comment_author_email']) ) 
  3406. return new IXR_Error( 403, __( 'A valid email address is required.' ) ); 
  3407.  
  3408. $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0; 
  3409.  
  3410. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3411. do_action( 'xmlrpc_call', 'wp.newComment' ); 
  3412.  
  3413. $comment_ID = wp_new_comment( $comment, true ); 
  3414. if ( is_wp_error( $comment_ID ) ) { 
  3415. return new IXR_Error( 403, $comment_ID->get_error_message() ); 
  3416.  
  3417. if ( ! $comment_ID ) { 
  3418. return new IXR_Error( 403, __( 'An unknown error occurred' ) ); 
  3419.  
  3420. /** 
  3421. * Fires after a new comment has been successfully created via XML-RPC. 
  3422. * 
  3423. * @since 3.4.0 
  3424. * 
  3425. * @param int $comment_ID ID of the new comment. 
  3426. * @param array $args An array of new comment arguments. 
  3427. */ 
  3428. do_action( 'xmlrpc_call_success_wp_newComment', $comment_ID, $args ); 
  3429.  
  3430. return $comment_ID; 
  3431.  
  3432. /** 
  3433. * Retrieve all of the comment status. 
  3434. * 
  3435. * @since 2.7.0 
  3436. * 
  3437. * @param array $args { 
  3438. * Method arguments. Note: arguments must be ordered as documented. 
  3439. * 
  3440. * @type int $blog_id (unused) 
  3441. * @type string $username 
  3442. * @type string $password 
  3443. * } 
  3444. * @return array|IXR_Error 
  3445. */ 
  3446. public function wp_getCommentStatusList( $args ) { 
  3447. $this->escape( $args ); 
  3448.  
  3449. $username = $args[1]; 
  3450. $password = $args[2]; 
  3451.  
  3452. if ( ! $user = $this->login( $username, $password ) ) { 
  3453. return $this->error; 
  3454.  
  3455. if ( ! current_user_can( 'publish_posts' ) ) { 
  3456. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3457.  
  3458. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3459. do_action( 'xmlrpc_call', 'wp.getCommentStatusList' ); 
  3460.  
  3461. return get_comment_statuses(); 
  3462.  
  3463. /** 
  3464. * Retrieve comment count. 
  3465. * 
  3466. * @since 2.5.0 
  3467. * 
  3468. * @param array $args { 
  3469. * Method arguments. Note: arguments must be ordered as documented. 
  3470. * 
  3471. * @type int $blog_id (unused) 
  3472. * @type string $username 
  3473. * @type string $password 
  3474. * @type int $post_id 
  3475. * } 
  3476. * @return array|IXR_Error 
  3477. */ 
  3478. public function wp_getCommentCount( $args ) { 
  3479. $this->escape( $args ); 
  3480.  
  3481. $username = $args[1]; 
  3482. $password = $args[2]; 
  3483. $post_id = (int) $args[3]; 
  3484.  
  3485. if ( ! $user = $this->login( $username, $password ) ) { 
  3486. return $this->error; 
  3487.  
  3488. $post = get_post( $post_id, ARRAY_A ); 
  3489. if ( empty( $post['ID'] ) ) { 
  3490. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3491.  
  3492. if ( ! current_user_can( 'edit_post', $post_id ) ) { 
  3493. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details of this post.' ) ); 
  3494.  
  3495. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3496. do_action( 'xmlrpc_call', 'wp.getCommentCount' ); 
  3497.  
  3498. $count = wp_count_comments( $post_id ); 
  3499.  
  3500. return array( 
  3501. 'approved' => $count->approved,  
  3502. 'awaiting_moderation' => $count->moderated,  
  3503. 'spam' => $count->spam,  
  3504. 'total_comments' => $count->total_comments 
  3505. ); 
  3506.  
  3507. /** 
  3508. * Retrieve post statuses. 
  3509. * 
  3510. * @since 2.5.0 
  3511. * 
  3512. * @param array $args { 
  3513. * Method arguments. Note: arguments must be ordered as documented. 
  3514. * 
  3515. * @type int $blog_id (unused) 
  3516. * @type string $username 
  3517. * @type string $password 
  3518. * } 
  3519. * @return array|IXR_Error 
  3520. */ 
  3521. public function wp_getPostStatusList( $args ) { 
  3522. $this->escape( $args ); 
  3523.  
  3524. $username = $args[1]; 
  3525. $password = $args[2]; 
  3526.  
  3527. if ( !$user = $this->login($username, $password) ) 
  3528. return $this->error; 
  3529.  
  3530. if ( !current_user_can( 'edit_posts' ) ) 
  3531. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3532.  
  3533. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3534. do_action( 'xmlrpc_call', 'wp.getPostStatusList' ); 
  3535.  
  3536. return get_post_statuses(); 
  3537.  
  3538. /** 
  3539. * Retrieve page statuses. 
  3540. * 
  3541. * @since 2.5.0 
  3542. * 
  3543. * @param array $args { 
  3544. * Method arguments. Note: arguments must be ordered as documented. 
  3545. * 
  3546. * @type int $blog_id (unused) 
  3547. * @type string $username 
  3548. * @type string $password 
  3549. * } 
  3550. * @return array|IXR_Error 
  3551. */ 
  3552. public function wp_getPageStatusList( $args ) { 
  3553. $this->escape( $args ); 
  3554.  
  3555. $username = $args[1]; 
  3556. $password = $args[2]; 
  3557.  
  3558. if ( !$user = $this->login($username, $password) ) 
  3559. return $this->error; 
  3560.  
  3561. if ( !current_user_can( 'edit_pages' ) ) 
  3562. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3563.  
  3564. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3565. do_action( 'xmlrpc_call', 'wp.getPageStatusList' ); 
  3566.  
  3567. return get_page_statuses(); 
  3568.  
  3569. /** 
  3570. * Retrieve page templates. 
  3571. * 
  3572. * @since 2.6.0 
  3573. * 
  3574. * @param array $args { 
  3575. * Method arguments. Note: arguments must be ordered as documented. 
  3576. * 
  3577. * @type int $blog_id (unused) 
  3578. * @type string $username 
  3579. * @type string $password 
  3580. * } 
  3581. * @return array|IXR_Error 
  3582. */ 
  3583. public function wp_getPageTemplates( $args ) { 
  3584. $this->escape( $args ); 
  3585.  
  3586. $username = $args[1]; 
  3587. $password = $args[2]; 
  3588.  
  3589. if ( !$user = $this->login($username, $password) ) 
  3590. return $this->error; 
  3591.  
  3592. if ( !current_user_can( 'edit_pages' ) ) 
  3593. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3594.  
  3595. $templates = get_page_templates(); 
  3596. $templates['Default'] = 'default'; 
  3597.  
  3598. return $templates; 
  3599.  
  3600. /** 
  3601. * Retrieve blog options. 
  3602. * 
  3603. * @since 2.6.0 
  3604. * 
  3605. * @param array $args { 
  3606. * Method arguments. Note: arguments must be ordered as documented. 
  3607. * 
  3608. * @type int $blog_id (unused) 
  3609. * @type string $username 
  3610. * @type string $password 
  3611. * @type array $options 
  3612. * } 
  3613. * @return array|IXR_Error 
  3614. */ 
  3615. public function wp_getOptions( $args ) { 
  3616. $this->escape( $args ); 
  3617.  
  3618. $username = $args[1]; 
  3619. $password = $args[2]; 
  3620. $options = isset( $args[3] ) ? (array) $args[3] : array(); 
  3621.  
  3622. if ( !$user = $this->login($username, $password) ) 
  3623. return $this->error; 
  3624.  
  3625. // If no specific options where asked for, return all of them 
  3626. if ( count( $options ) == 0 ) 
  3627. $options = array_keys($this->blog_options); 
  3628.  
  3629. return $this->_getOptions($options); 
  3630.  
  3631. /** 
  3632. * Retrieve blog options value from list. 
  3633. * 
  3634. * @since 2.6.0 
  3635. * 
  3636. * @param array $options Options to retrieve. 
  3637. * @return array 
  3638. */ 
  3639. public function _getOptions($options) { 
  3640. $data = array(); 
  3641. $can_manage = current_user_can( 'manage_options' ); 
  3642. foreach ( $options as $option ) { 
  3643. if ( array_key_exists( $option, $this->blog_options ) ) { 
  3644. $data[$option] = $this->blog_options[$option]; 
  3645. //Is the value static or dynamic? 
  3646. if ( isset( $data[$option]['option'] ) ) { 
  3647. $data[$option]['value'] = get_option( $data[$option]['option'] ); 
  3648. unset($data[$option]['option']); 
  3649.  
  3650. if ( ! $can_manage ) 
  3651. $data[$option]['readonly'] = true; 
  3652.  
  3653. return $data; 
  3654.  
  3655. /** 
  3656. * Update blog options. 
  3657. * 
  3658. * @since 2.6.0 
  3659. * 
  3660. * @param array $args { 
  3661. * Method arguments. Note: arguments must be ordered as documented. 
  3662. * 
  3663. * @type int $blog_id (unused) 
  3664. * @type string $username 
  3665. * @type string $password 
  3666. * @type array $options 
  3667. * } 
  3668. * @return array|IXR_Error 
  3669. */ 
  3670. public function wp_setOptions( $args ) { 
  3671. $this->escape( $args ); 
  3672.  
  3673. $username = $args[1]; 
  3674. $password = $args[2]; 
  3675. $options = (array) $args[3]; 
  3676.  
  3677. if ( !$user = $this->login($username, $password) ) 
  3678. return $this->error; 
  3679.  
  3680. if ( !current_user_can( 'manage_options' ) ) 
  3681. return new IXR_Error( 403, __( 'Sorry, you are not allowed to update options.' ) ); 
  3682.  
  3683. $option_names = array(); 
  3684. foreach ( $options as $o_name => $o_value ) { 
  3685. $option_names[] = $o_name; 
  3686. if ( !array_key_exists( $o_name, $this->blog_options ) ) 
  3687. continue; 
  3688.  
  3689. if ( $this->blog_options[$o_name]['readonly'] == true ) 
  3690. continue; 
  3691.  
  3692. update_option( $this->blog_options[$o_name]['option'], wp_unslash( $o_value ) ); 
  3693.  
  3694. //Now return the updated values 
  3695. return $this->_getOptions($option_names); 
  3696.  
  3697. /** 
  3698. * Retrieve a media item by ID 
  3699. * 
  3700. * @since 3.1.0 
  3701. * 
  3702. * @param array $args { 
  3703. * Method arguments. Note: arguments must be ordered as documented. 
  3704. * 
  3705. * @type int $blog_id (unused) 
  3706. * @type string $username 
  3707. * @type string $password 
  3708. * @type int $attachment_id 
  3709. * } 
  3710. * @return array|IXR_Error Associative array contains: 
  3711. * - 'date_created_gmt' 
  3712. * - 'parent' 
  3713. * - 'link' 
  3714. * - 'thumbnail' 
  3715. * - 'title' 
  3716. * - 'caption' 
  3717. * - 'description' 
  3718. * - 'metadata' 
  3719. */ 
  3720. public function wp_getMediaItem( $args ) { 
  3721. $this->escape( $args ); 
  3722.  
  3723. $username = $args[1]; 
  3724. $password = $args[2]; 
  3725. $attachment_id = (int) $args[3]; 
  3726.  
  3727. if ( !$user = $this->login($username, $password) ) 
  3728. return $this->error; 
  3729.  
  3730. if ( !current_user_can( 'upload_files' ) ) 
  3731. return new IXR_Error( 403, __( 'Sorry, you are not allowed to upload files.' ) ); 
  3732.  
  3733. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3734. do_action( 'xmlrpc_call', 'wp.getMediaItem' ); 
  3735.  
  3736. if ( ! $attachment = get_post($attachment_id) ) 
  3737. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  3738.  
  3739. return $this->_prepare_media_item( $attachment ); 
  3740.  
  3741. /** 
  3742. * Retrieves a collection of media library items (or attachments) 
  3743. * 
  3744. * Besides the common blog_id (unused), username, and password arguments, it takes a filter 
  3745. * array as last argument. 
  3746. * 
  3747. * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'. 
  3748. * 
  3749. * The defaults are as follows: 
  3750. * - 'number' - Default is 5. Total number of media items to retrieve. 
  3751. * - 'offset' - Default is 0. See WP_Query::query() for more. 
  3752. * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items. 
  3753. * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf') 
  3754. * 
  3755. * @since 3.1.0 
  3756. * 
  3757. * @param array $args { 
  3758. * Method arguments. Note: arguments must be ordered as documented. 
  3759. * 
  3760. * @type int $blog_id (unused) 
  3761. * @type string $username 
  3762. * @type string $password 
  3763. * @type array $struct 
  3764. * } 
  3765. * @return array|IXR_Error Contains a collection of media items. See wp_xmlrpc_server::wp_getMediaItem() for a description of each item contents 
  3766. */ 
  3767. public function wp_getMediaLibrary($args) { 
  3768. $this->escape($args); 
  3769.  
  3770. $username = $args[1]; 
  3771. $password = $args[2]; 
  3772. $struct = isset( $args[3] ) ? $args[3] : array() ; 
  3773.  
  3774. if ( !$user = $this->login($username, $password) ) 
  3775. return $this->error; 
  3776.  
  3777. if ( !current_user_can( 'upload_files' ) ) 
  3778. return new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); 
  3779.  
  3780. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3781. do_action( 'xmlrpc_call', 'wp.getMediaLibrary' ); 
  3782.  
  3783. $parent_id = ( isset($struct['parent_id']) ) ? absint($struct['parent_id']) : '' ; 
  3784. $mime_type = ( isset($struct['mime_type']) ) ? $struct['mime_type'] : '' ; 
  3785. $offset = ( isset($struct['offset']) ) ? absint($struct['offset']) : 0 ; 
  3786. $number = ( isset($struct['number']) ) ? absint($struct['number']) : -1 ; 
  3787.  
  3788. $attachments = get_posts( array('post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type ) ); 
  3789.  
  3790. $attachments_struct = array(); 
  3791.  
  3792. foreach ($attachments as $attachment ) 
  3793. $attachments_struct[] = $this->_prepare_media_item( $attachment ); 
  3794.  
  3795. return $attachments_struct; 
  3796.  
  3797. /** 
  3798. * Retrieves a list of post formats used by the site. 
  3799. * 
  3800. * @since 3.1.0 
  3801. * 
  3802. * @param array $args { 
  3803. * Method arguments. Note: arguments must be ordered as documented. 
  3804. * 
  3805. * @type int $blog_id (unused) 
  3806. * @type string $username 
  3807. * @type string $password 
  3808. * } 
  3809. * @return array|IXR_Error List of post formats, otherwise IXR_Error object. 
  3810. */ 
  3811. public function wp_getPostFormats( $args ) { 
  3812. $this->escape( $args ); 
  3813.  
  3814. $username = $args[1]; 
  3815. $password = $args[2]; 
  3816.  
  3817. if ( !$user = $this->login( $username, $password ) ) 
  3818. return $this->error; 
  3819.  
  3820. if ( !current_user_can( 'edit_posts' ) ) 
  3821. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3822.  
  3823. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3824. do_action( 'xmlrpc_call', 'wp.getPostFormats' ); 
  3825.  
  3826. $formats = get_post_format_strings(); 
  3827.  
  3828. // find out if they want a list of currently supports formats 
  3829. if ( isset( $args[3] ) && is_array( $args[3] ) ) { 
  3830. if ( $args[3]['show-supported'] ) { 
  3831. if ( current_theme_supports( 'post-formats' ) ) { 
  3832. $supported = get_theme_support( 'post-formats' ); 
  3833.  
  3834. $data = array(); 
  3835. $data['all'] = $formats; 
  3836. $data['supported'] = $supported[0]; 
  3837.  
  3838. $formats = $data; 
  3839.  
  3840. return $formats; 
  3841.  
  3842. /** 
  3843. * Retrieves a post type 
  3844. * 
  3845. * @since 3.4.0 
  3846. * 
  3847. * @see get_post_type_object() 
  3848. * 
  3849. * @param array $args { 
  3850. * Method arguments. Note: arguments must be ordered as documented. 
  3851. * 
  3852. * @type int $blog_id (unused) 
  3853. * @type string $username 
  3854. * @type string $password 
  3855. * @type string $post_type_name 
  3856. * @type array $fields (optional) 
  3857. * } 
  3858. * @return array|IXR_Error Array contains: 
  3859. * - 'labels' 
  3860. * - 'description' 
  3861. * - 'capability_type' 
  3862. * - 'cap' 
  3863. * - 'map_meta_cap' 
  3864. * - 'hierarchical' 
  3865. * - 'menu_position' 
  3866. * - 'taxonomies' 
  3867. * - 'supports' 
  3868. */ 
  3869. public function wp_getPostType( $args ) { 
  3870. if ( ! $this->minimum_args( $args, 4 ) ) 
  3871. return $this->error; 
  3872.  
  3873. $this->escape( $args ); 
  3874.  
  3875. $username = $args[1]; 
  3876. $password = $args[2]; 
  3877. $post_type_name = $args[3]; 
  3878.  
  3879. if ( isset( $args[4] ) ) { 
  3880. $fields = $args[4]; 
  3881. } else { 
  3882. /** 
  3883. * Filters the default query fields used by the given XML-RPC method. 
  3884. * 
  3885. * @since 3.4.0 
  3886. * 
  3887. * @param array $fields An array of post type query fields for the given method. 
  3888. * @param string $method The method name. 
  3889. */ 
  3890. $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' ); 
  3891.  
  3892. if ( !$user = $this->login( $username, $password ) ) 
  3893. return $this->error; 
  3894.  
  3895. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3896. do_action( 'xmlrpc_call', 'wp.getPostType' ); 
  3897.  
  3898. if ( ! post_type_exists( $post_type_name ) ) 
  3899. return new IXR_Error( 403, __( 'Invalid post type.' ) ); 
  3900.  
  3901. $post_type = get_post_type_object( $post_type_name ); 
  3902.  
  3903. if ( ! current_user_can( $post_type->cap->edit_posts ) ) 
  3904. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); 
  3905.  
  3906. return $this->_prepare_post_type( $post_type, $fields ); 
  3907.  
  3908. /** 
  3909. * Retrieves a post types 
  3910. * 
  3911. * @since 3.4.0 
  3912. * 
  3913. * @see get_post_types() 
  3914. * 
  3915. * @param array $args { 
  3916. * Method arguments. Note: arguments must be ordered as documented. 
  3917. * 
  3918. * @type int $blog_id (unused) 
  3919. * @type string $username 
  3920. * @type string $password 
  3921. * @type array $filter (optional) 
  3922. * @type array $fields (optional) 
  3923. * } 
  3924. * @return array|IXR_Error 
  3925. */ 
  3926. public function wp_getPostTypes( $args ) { 
  3927. if ( ! $this->minimum_args( $args, 3 ) ) 
  3928. return $this->error; 
  3929.  
  3930. $this->escape( $args ); 
  3931.  
  3932. $username = $args[1]; 
  3933. $password = $args[2]; 
  3934. $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); 
  3935.  
  3936. if ( isset( $args[4] ) ) { 
  3937. $fields = $args[4]; 
  3938. } else { 
  3939. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3940. $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' ); 
  3941.  
  3942. if ( ! $user = $this->login( $username, $password ) ) 
  3943. return $this->error; 
  3944.  
  3945. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3946. do_action( 'xmlrpc_call', 'wp.getPostTypes' ); 
  3947.  
  3948. $post_types = get_post_types( $filter, 'objects' ); 
  3949.  
  3950. $struct = array(); 
  3951.  
  3952. foreach ( $post_types as $post_type ) { 
  3953. if ( ! current_user_can( $post_type->cap->edit_posts ) ) 
  3954. continue; 
  3955.  
  3956. $struct[$post_type->name] = $this->_prepare_post_type( $post_type, $fields ); 
  3957.  
  3958. return $struct; 
  3959.  
  3960. /** 
  3961. * Retrieve revisions for a specific post. 
  3962. * 
  3963. * @since 3.5.0 
  3964. * 
  3965. * The optional $fields parameter specifies what fields will be included 
  3966. * in the response array. 
  3967. * 
  3968. * @uses wp_get_post_revisions() 
  3969. * @see wp_getPost() for more on $fields 
  3970. * 
  3971. * @param array $args { 
  3972. * Method arguments. Note: arguments must be ordered as documented. 
  3973. * 
  3974. * @type int $blog_id (unused) 
  3975. * @type string $username 
  3976. * @type string $password 
  3977. * @type int $post_id 
  3978. * @type array $fields (optional) 
  3979. * } 
  3980. * @return array|IXR_Error contains a collection of posts. 
  3981. */ 
  3982. public function wp_getRevisions( $args ) { 
  3983. if ( ! $this->minimum_args( $args, 4 ) ) 
  3984. return $this->error; 
  3985.  
  3986. $this->escape( $args ); 
  3987.  
  3988. $username = $args[1]; 
  3989. $password = $args[2]; 
  3990. $post_id = (int) $args[3]; 
  3991.  
  3992. if ( isset( $args[4] ) ) { 
  3993. $fields = $args[4]; 
  3994. } else { 
  3995. /** 
  3996. * Filters the default revision query fields used by the given XML-RPC method. 
  3997. * 
  3998. * @since 3.5.0 
  3999. * 
  4000. * @param array $field An array of revision query fields. 
  4001. * @param string $method The method name. 
  4002. */ 
  4003. $fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' ); 
  4004.  
  4005. if ( ! $user = $this->login( $username, $password ) ) 
  4006. return $this->error; 
  4007.  
  4008. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4009. do_action( 'xmlrpc_call', 'wp.getRevisions' ); 
  4010.  
  4011. if ( ! $post = get_post( $post_id ) ) 
  4012. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4013.  
  4014. if ( ! current_user_can( 'edit_post', $post_id ) ) 
  4015. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  4016.  
  4017. // Check if revisions are enabled. 
  4018. if ( ! wp_revisions_enabled( $post ) ) 
  4019. return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); 
  4020.  
  4021. $revisions = wp_get_post_revisions( $post_id ); 
  4022.  
  4023. if ( ! $revisions ) 
  4024. return array(); 
  4025.  
  4026. $struct = array(); 
  4027.  
  4028. foreach ( $revisions as $revision ) { 
  4029. if ( ! current_user_can( 'read_post', $revision->ID ) ) 
  4030. continue; 
  4031.  
  4032. // Skip autosaves 
  4033. if ( wp_is_post_autosave( $revision ) ) 
  4034. continue; 
  4035.  
  4036. $struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields ); 
  4037.  
  4038. return $struct; 
  4039.  
  4040. /** 
  4041. * Restore a post revision 
  4042. * 
  4043. * @since 3.5.0 
  4044. * 
  4045. * @uses wp_restore_post_revision() 
  4046. * 
  4047. * @param array $args { 
  4048. * Method arguments. Note: arguments must be ordered as documented. 
  4049. * 
  4050. * @type int $blog_id (unused) 
  4051. * @type string $username 
  4052. * @type string $password 
  4053. * @type int $revision_id 
  4054. * } 
  4055. * @return bool|IXR_Error false if there was an error restoring, true if success. 
  4056. */ 
  4057. public function wp_restoreRevision( $args ) { 
  4058. if ( ! $this->minimum_args( $args, 3 ) ) 
  4059. return $this->error; 
  4060.  
  4061. $this->escape( $args ); 
  4062.  
  4063. $username = $args[1]; 
  4064. $password = $args[2]; 
  4065. $revision_id = (int) $args[3]; 
  4066.  
  4067. if ( ! $user = $this->login( $username, $password ) ) 
  4068. return $this->error; 
  4069.  
  4070. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4071. do_action( 'xmlrpc_call', 'wp.restoreRevision' ); 
  4072.  
  4073. if ( ! $revision = wp_get_post_revision( $revision_id ) ) 
  4074. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4075.  
  4076. if ( wp_is_post_autosave( $revision ) ) 
  4077. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4078.  
  4079. if ( ! $post = get_post( $revision->post_parent ) ) 
  4080. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4081.  
  4082. if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) 
  4083. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  4084.  
  4085. // Check if revisions are disabled. 
  4086. if ( ! wp_revisions_enabled( $post ) ) 
  4087. return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); 
  4088.  
  4089. $post = wp_restore_post_revision( $revision_id ); 
  4090.  
  4091. return (bool) $post; 
  4092.  
  4093. /** Blogger API functions. 
  4094. * specs on http://plant.blogger.com/api and https://groups.yahoo.com/group/bloggerDev/ 
  4095. */ 
  4096.  
  4097. /** 
  4098. * Retrieve blogs that user owns. 
  4099. * 
  4100. * Will make more sense once we support multiple blogs. 
  4101. * 
  4102. * @since 1.5.0 
  4103. * 
  4104. * @param array $args { 
  4105. * Method arguments. Note: arguments must be ordered as documented. 
  4106. * 
  4107. * @type int $blog_id (unused) 
  4108. * @type string $username 
  4109. * @type string $password 
  4110. * } 
  4111. * @return array|IXR_Error 
  4112. */ 
  4113. public function blogger_getUsersBlogs($args) { 
  4114. if ( ! $this->minimum_args( $args, 3 ) ) { 
  4115. return $this->error; 
  4116.  
  4117. if ( is_multisite() ) { 
  4118. return $this->_multisite_getUsersBlogs($args); 
  4119.  
  4120. $this->escape($args); 
  4121.  
  4122. $username = $args[1]; 
  4123. $password = $args[2]; 
  4124.  
  4125. if ( !$user = $this->login($username, $password) ) 
  4126. return $this->error; 
  4127.  
  4128. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4129. do_action( 'xmlrpc_call', 'blogger.getUsersBlogs' ); 
  4130.  
  4131. $is_admin = current_user_can('manage_options'); 
  4132.  
  4133. $struct = array( 
  4134. 'isAdmin' => $is_admin,  
  4135. 'url' => get_option('home') . '/',  
  4136. 'blogid' => '1',  
  4137. 'blogName' => get_option('blogname'),  
  4138. 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),  
  4139. ); 
  4140.  
  4141. return array($struct); 
  4142.  
  4143. /** 
  4144. * Private function for retrieving a users blogs for multisite setups 
  4145. * 
  4146. * @since 3.0.0 
  4147. * @access protected 
  4148. * 
  4149. * @param array $args { 
  4150. * Method arguments. Note: arguments must be ordered as documented. 
  4151. * 
  4152. * @type string $username Username. 
  4153. * @type string $password Password. 
  4154. * } 
  4155. * @return array|IXR_Error 
  4156. */ 
  4157. protected function _multisite_getUsersBlogs( $args ) { 
  4158. $current_blog = get_site(); 
  4159.  
  4160. $domain = $current_blog->domain; 
  4161. $path = $current_blog->path . 'xmlrpc.php'; 
  4162.  
  4163. $rpc = new IXR_Client( set_url_scheme( "http://{$domain}{$path}" ) ); 
  4164. $rpc->query('wp.getUsersBlogs', $args[1], $args[2]); 
  4165. $blogs = $rpc->getResponse(); 
  4166.  
  4167. if ( isset($blogs['faultCode']) ) 
  4168. return new IXR_Error($blogs['faultCode'], $blogs['faultString']); 
  4169.  
  4170. if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { 
  4171. return $blogs; 
  4172. } else { 
  4173. foreach ( (array) $blogs as $blog ) { 
  4174. if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) ) 
  4175. return array($blog); 
  4176. return array(); 
  4177.  
  4178. /** 
  4179. * Retrieve user's data. 
  4180. * 
  4181. * Gives your client some info about you, so you don't have to. 
  4182. * 
  4183. * @since 1.5.0 
  4184. * 
  4185. * @param array $args { 
  4186. * Method arguments. Note: arguments must be ordered as documented. 
  4187. * 
  4188. * @type int $blog_id (unused) 
  4189. * @type string $username 
  4190. * @type string $password 
  4191. * } 
  4192. * @return array|IXR_Error 
  4193. */ 
  4194. public function blogger_getUserInfo( $args ) { 
  4195. $this->escape( $args ); 
  4196.  
  4197. $username = $args[1]; 
  4198. $password = $args[2]; 
  4199.  
  4200. if ( !$user = $this->login($username, $password) ) 
  4201. return $this->error; 
  4202.  
  4203. if ( !current_user_can( 'edit_posts' ) ) 
  4204. return new IXR_Error( 401, __( 'Sorry, you are not allowed to access user data on this site.' ) ); 
  4205.  
  4206. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4207. do_action( 'xmlrpc_call', 'blogger.getUserInfo' ); 
  4208.  
  4209. $struct = array( 
  4210. 'nickname' => $user->nickname,  
  4211. 'userid' => $user->ID,  
  4212. 'url' => $user->user_url,  
  4213. 'lastname' => $user->last_name,  
  4214. 'firstname' => $user->first_name 
  4215. ); 
  4216.  
  4217. return $struct; 
  4218.  
  4219. /** 
  4220. * Retrieve post. 
  4221. * 
  4222. * @since 1.5.0 
  4223. * 
  4224. * @param array $args { 
  4225. * Method arguments. Note: arguments must be ordered as documented. 
  4226. * 
  4227. * @type int $blog_id (unused) 
  4228. * @type int $post_ID 
  4229. * @type string $username 
  4230. * @type string $password 
  4231. * } 
  4232. * @return array|IXR_Error 
  4233. */ 
  4234. public function blogger_getPost( $args ) { 
  4235. $this->escape( $args ); 
  4236.  
  4237. $post_ID = (int) $args[1]; 
  4238. $username = $args[2]; 
  4239. $password = $args[3]; 
  4240.  
  4241. if ( !$user = $this->login($username, $password) ) 
  4242. return $this->error; 
  4243.  
  4244. $post_data = get_post($post_ID, ARRAY_A); 
  4245. if ( ! $post_data ) 
  4246. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4247.  
  4248. if ( !current_user_can( 'edit_post', $post_ID ) ) 
  4249. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  4250.  
  4251. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4252. do_action( 'xmlrpc_call', 'blogger.getPost' ); 
  4253.  
  4254. $categories = implode(', ', wp_get_post_categories($post_ID)); 
  4255.  
  4256. $content = '<title>'.wp_unslash($post_data['post_title']).'</title>'; 
  4257. $content .= '<category>'.$categories.'</category>'; 
  4258. $content .= wp_unslash($post_data['post_content']); 
  4259.  
  4260. $struct = array( 
  4261. 'userid' => $post_data['post_author'],  
  4262. 'dateCreated' => $this->_convert_date( $post_data['post_date'] ),  
  4263. 'content' => $content,  
  4264. 'postid' => (string) $post_data['ID'] 
  4265. ); 
  4266.  
  4267. return $struct; 
  4268.  
  4269. /** 
  4270. * Retrieve list of recent posts. 
  4271. * 
  4272. * @since 1.5.0 
  4273. * 
  4274. * @param array $args { 
  4275. * Method arguments. Note: arguments must be ordered as documented. 
  4276. * 
  4277. * @type string $appkey (unused) 
  4278. * @type int $blog_id (unused) 
  4279. * @type string $username 
  4280. * @type string $password 
  4281. * @type int $numberposts (optional) 
  4282. * } 
  4283. * @return array|IXR_Error 
  4284. */ 
  4285. public function blogger_getRecentPosts( $args ) { 
  4286.  
  4287. $this->escape($args); 
  4288.  
  4289. // $args[0] = appkey - ignored 
  4290. $username = $args[2]; 
  4291. $password = $args[3]; 
  4292. if ( isset( $args[4] ) ) 
  4293. $query = array( 'numberposts' => absint( $args[4] ) ); 
  4294. else 
  4295. $query = array(); 
  4296.  
  4297. if ( !$user = $this->login($username, $password) ) 
  4298. return $this->error; 
  4299.  
  4300. if ( ! current_user_can( 'edit_posts' ) ) 
  4301. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  4302.  
  4303. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4304. do_action( 'xmlrpc_call', 'blogger.getRecentPosts' ); 
  4305.  
  4306. $posts_list = wp_get_recent_posts( $query ); 
  4307.  
  4308. if ( !$posts_list ) { 
  4309. $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 
  4310. return $this->error; 
  4311.  
  4312. $recent_posts = array(); 
  4313. foreach ($posts_list as $entry) { 
  4314. if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 
  4315. continue; 
  4316.  
  4317. $post_date = $this->_convert_date( $entry['post_date'] ); 
  4318. $categories = implode(', ', wp_get_post_categories($entry['ID'])); 
  4319.  
  4320. $content = '<title>'.wp_unslash($entry['post_title']).'</title>'; 
  4321. $content .= '<category>'.$categories.'</category>'; 
  4322. $content .= wp_unslash($entry['post_content']); 
  4323.  
  4324. $recent_posts[] = array( 
  4325. 'userid' => $entry['post_author'],  
  4326. 'dateCreated' => $post_date,  
  4327. 'content' => $content,  
  4328. 'postid' => (string) $entry['ID'],  
  4329. ); 
  4330.  
  4331. return $recent_posts; 
  4332.  
  4333. /** 
  4334. * Deprecated. 
  4335. * 
  4336. * @since 1.5.0 
  4337. * @deprecated 3.5.0 
  4338. * 
  4339. * @param array $args Unused. 
  4340. * @return IXR_Error Error object. 
  4341. */ 
  4342. public function blogger_getTemplate($args) { 
  4343. return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) ); 
  4344.  
  4345. /** 
  4346. * Deprecated. 
  4347. * 
  4348. * @since 1.5.0 
  4349. * @deprecated 3.5.0 
  4350. * 
  4351. * @param array $args Unused. 
  4352. * @return IXR_Error Error object. 
  4353. */ 
  4354. public function blogger_setTemplate($args) { 
  4355. return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) ); 
  4356.  
  4357. /** 
  4358. * Creates new post. 
  4359. * 
  4360. * @since 1.5.0 
  4361. * 
  4362. * @param array $args { 
  4363. * Method arguments. Note: arguments must be ordered as documented. 
  4364. * 
  4365. * @type string $appkey (unused) 
  4366. * @type int $blog_id (unused) 
  4367. * @type string $username 
  4368. * @type string $password 
  4369. * @type string $content 
  4370. * @type string $publish 
  4371. * } 
  4372. * @return int|IXR_Error 
  4373. */ 
  4374. public function blogger_newPost( $args ) { 
  4375. $this->escape( $args ); 
  4376.  
  4377. $username = $args[2]; 
  4378. $password = $args[3]; 
  4379. $content = $args[4]; 
  4380. $publish = $args[5]; 
  4381.  
  4382. if ( !$user = $this->login($username, $password) ) 
  4383. return $this->error; 
  4384.  
  4385. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4386. do_action( 'xmlrpc_call', 'blogger.newPost' ); 
  4387.  
  4388. $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 
  4389. if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || !current_user_can($cap) ) 
  4390. return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.')); 
  4391.  
  4392. $post_status = ($publish) ? 'publish' : 'draft'; 
  4393.  
  4394. $post_author = $user->ID; 
  4395.  
  4396. $post_title = xmlrpc_getposttitle($content); 
  4397. $post_category = xmlrpc_getpostcategory($content); 
  4398. $post_content = xmlrpc_removepostdata($content); 
  4399.  
  4400. $post_date = current_time('mysql'); 
  4401. $post_date_gmt = current_time('mysql', 1); 
  4402.  
  4403. $post_data = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); 
  4404.  
  4405. $post_ID = wp_insert_post($post_data); 
  4406. if ( is_wp_error( $post_ID ) ) 
  4407. return new IXR_Error(500, $post_ID->get_error_message()); 
  4408.  
  4409. if ( !$post_ID ) 
  4410. return new IXR_Error(500, __('Sorry, your entry could not be posted.')); 
  4411.  
  4412. $this->attach_uploads( $post_ID, $post_content ); 
  4413.  
  4414. /** 
  4415. * Fires after a new post has been successfully created via the XML-RPC Blogger API. 
  4416. * 
  4417. * @since 3.4.0 
  4418. * 
  4419. * @param int $post_ID ID of the new post. 
  4420. * @param array $args An array of new post arguments. 
  4421. */ 
  4422. do_action( 'xmlrpc_call_success_blogger_newPost', $post_ID, $args ); 
  4423.  
  4424. return $post_ID; 
  4425.  
  4426. /** 
  4427. * Edit a post. 
  4428. * 
  4429. * @since 1.5.0 
  4430. * 
  4431. * @param array $args { 
  4432. * Method arguments. Note: arguments must be ordered as documented. 
  4433. * 
  4434. * @type int $blog_id (unused) 
  4435. * @type int $post_ID 
  4436. * @type string $username 
  4437. * @type string $password 
  4438. * @type string $content 
  4439. * @type bool $publish 
  4440. * } 
  4441. * @return true|IXR_Error true when done. 
  4442. */ 
  4443. public function blogger_editPost( $args ) { 
  4444.  
  4445. $this->escape($args); 
  4446.  
  4447. $post_ID = (int) $args[1]; 
  4448. $username = $args[2]; 
  4449. $password = $args[3]; 
  4450. $content = $args[4]; 
  4451. $publish = $args[5]; 
  4452.  
  4453. if ( ! $user = $this->login( $username, $password ) ) { 
  4454. return $this->error; 
  4455.  
  4456. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4457. do_action( 'xmlrpc_call', 'blogger.editPost' ); 
  4458.  
  4459. $actual_post = get_post( $post_ID, ARRAY_A ); 
  4460.  
  4461. if ( ! $actual_post || $actual_post['post_type'] != 'post' ) { 
  4462. return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); 
  4463.  
  4464. $this->escape($actual_post); 
  4465.  
  4466. if ( ! current_user_can( 'edit_post', $post_ID ) ) { 
  4467. return new IXR_Error(401, __('Sorry, you are not allowed to edit this post.')); 
  4468. if ( 'publish' == $actual_post['post_status'] && ! current_user_can( 'publish_posts' ) ) { 
  4469. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); 
  4470.  
  4471. $postdata = array(); 
  4472. $postdata['ID'] = $actual_post['ID']; 
  4473. $postdata['post_content'] = xmlrpc_removepostdata( $content ); 
  4474. $postdata['post_title'] = xmlrpc_getposttitle( $content ); 
  4475. $postdata['post_category'] = xmlrpc_getpostcategory( $content ); 
  4476. $postdata['post_status'] = $actual_post['post_status']; 
  4477. $postdata['post_excerpt'] = $actual_post['post_excerpt']; 
  4478. $postdata['post_status'] = $publish ? 'publish' : 'draft'; 
  4479.  
  4480. $result = wp_update_post( $postdata ); 
  4481.  
  4482. if ( ! $result ) { 
  4483. return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); 
  4484. $this->attach_uploads( $actual_post['ID'], $postdata['post_content'] ); 
  4485.  
  4486. /** 
  4487. * Fires after a post has been successfully updated via the XML-RPC Blogger API. 
  4488. * 
  4489. * @since 3.4.0 
  4490. * 
  4491. * @param int $post_ID ID of the updated post. 
  4492. * @param array $args An array of arguments for the post to edit. 
  4493. */ 
  4494. do_action( 'xmlrpc_call_success_blogger_editPost', $post_ID, $args ); 
  4495.  
  4496. return true; 
  4497.  
  4498. /** 
  4499. * Remove a post. 
  4500. * 
  4501. * @since 1.5.0 
  4502. * 
  4503. * @param array $args { 
  4504. * Method arguments. Note: arguments must be ordered as documented. 
  4505. * 
  4506. * @type int $blog_id (unused) 
  4507. * @type int $post_ID 
  4508. * @type string $username 
  4509. * @type string $password 
  4510. * } 
  4511. * @return true|IXR_Error True when post is deleted. 
  4512. */ 
  4513. public function blogger_deletePost( $args ) { 
  4514. $this->escape( $args ); 
  4515.  
  4516. $post_ID = (int) $args[1]; 
  4517. $username = $args[2]; 
  4518. $password = $args[3]; 
  4519.  
  4520. if ( !$user = $this->login($username, $password) ) 
  4521. return $this->error; 
  4522.  
  4523. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4524. do_action( 'xmlrpc_call', 'blogger.deletePost' ); 
  4525.  
  4526. $actual_post = get_post( $post_ID, ARRAY_A ); 
  4527.  
  4528. if ( ! $actual_post || $actual_post['post_type'] != 'post' ) { 
  4529. return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); 
  4530.  
  4531. if ( ! current_user_can( 'delete_post', $post_ID ) ) { 
  4532. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); 
  4533.  
  4534. $result = wp_delete_post( $post_ID ); 
  4535.  
  4536. if ( ! $result ) { 
  4537. return new IXR_Error( 500, __( 'The post cannot be deleted.' ) ); 
  4538.  
  4539. /** 
  4540. * Fires after a post has been successfully deleted via the XML-RPC Blogger API. 
  4541. * 
  4542. * @since 3.4.0 
  4543. * 
  4544. * @param int $post_ID ID of the deleted post. 
  4545. * @param array $args An array of arguments to delete the post. 
  4546. */ 
  4547. do_action( 'xmlrpc_call_success_blogger_deletePost', $post_ID, $args ); 
  4548.  
  4549. return true; 
  4550.  
  4551. /** MetaWeblog API functions 
  4552. * specs on wherever Dave Winer wants them to be 
  4553. */ 
  4554.  
  4555. /** 
  4556. * Create a new post. 
  4557. * 
  4558. * The 'content_struct' argument must contain: 
  4559. * - title 
  4560. * - description 
  4561. * - mt_excerpt 
  4562. * - mt_text_more 
  4563. * - mt_keywords 
  4564. * - mt_tb_ping_urls 
  4565. * - categories 
  4566. * 
  4567. * Also, it can optionally contain: 
  4568. * - wp_slug 
  4569. * - wp_password 
  4570. * - wp_page_parent_id 
  4571. * - wp_page_order 
  4572. * - wp_author_id 
  4573. * - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending' 
  4574. * - mt_allow_comments - can be 'open' or 'closed' 
  4575. * - mt_allow_pings - can be 'open' or 'closed' 
  4576. * - date_created_gmt 
  4577. * - dateCreated 
  4578. * - wp_post_thumbnail 
  4579. * 
  4580. * @since 1.5.0 
  4581. * 
  4582. * @param array $args { 
  4583. * Method arguments. Note: arguments must be ordered as documented. 
  4584. * 
  4585. * @type int $blog_id (unused) 
  4586. * @type string $username 
  4587. * @type string $password 
  4588. * @type array $content_struct 
  4589. * @type int $publish 
  4590. * } 
  4591. * @return int|IXR_Error 
  4592. */ 
  4593. public function mw_newPost($args) { 
  4594. $this->escape($args); 
  4595.  
  4596. $username = $args[1]; 
  4597. $password = $args[2]; 
  4598. $content_struct = $args[3]; 
  4599. $publish = isset( $args[4] ) ? $args[4] : 0; 
  4600.  
  4601. if ( !$user = $this->login($username, $password) ) 
  4602. return $this->error; 
  4603.  
  4604. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4605. do_action( 'xmlrpc_call', 'metaWeblog.newPost' ); 
  4606.  
  4607. $page_template = ''; 
  4608. if ( !empty( $content_struct['post_type'] ) ) { 
  4609. if ( $content_struct['post_type'] == 'page' ) { 
  4610. if ( $publish ) 
  4611. $cap = 'publish_pages'; 
  4612. elseif ( isset( $content_struct['page_status'] ) && 'publish' == $content_struct['page_status'] ) 
  4613. $cap = 'publish_pages'; 
  4614. else 
  4615. $cap = 'edit_pages'; 
  4616. $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); 
  4617. $post_type = 'page'; 
  4618. if ( !empty( $content_struct['wp_page_template'] ) ) 
  4619. $page_template = $content_struct['wp_page_template']; 
  4620. } elseif ( $content_struct['post_type'] == 'post' ) { 
  4621. if ( $publish ) 
  4622. $cap = 'publish_posts'; 
  4623. elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'] ) 
  4624. $cap = 'publish_posts'; 
  4625. else 
  4626. $cap = 'edit_posts'; 
  4627. $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 
  4628. $post_type = 'post'; 
  4629. } else { 
  4630. // No other post_type values are allowed here 
  4631. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4632. } else { 
  4633. if ( $publish ) 
  4634. $cap = 'publish_posts'; 
  4635. elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status']) 
  4636. $cap = 'publish_posts'; 
  4637. else 
  4638. $cap = 'edit_posts'; 
  4639. $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 
  4640. $post_type = 'post'; 
  4641.  
  4642. if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) ) 
  4643. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) ); 
  4644. if ( !current_user_can( $cap ) ) 
  4645. return new IXR_Error( 401, $error_message ); 
  4646.  
  4647. // Check for a valid post format if one was given 
  4648. if ( isset( $content_struct['wp_post_format'] ) ) { 
  4649. $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); 
  4650. if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { 
  4651. return new IXR_Error( 404, __( 'Invalid post format.' ) ); 
  4652.  
  4653. // Let WordPress generate the post_name (slug) unless 
  4654. // one has been provided. 
  4655. $post_name = ""; 
  4656. if ( isset($content_struct['wp_slug']) ) 
  4657. $post_name = $content_struct['wp_slug']; 
  4658.  
  4659. // Only use a password if one was given. 
  4660. if ( isset($content_struct['wp_password']) ) 
  4661. $post_password = $content_struct['wp_password']; 
  4662.  
  4663. // Only set a post parent if one was provided. 
  4664. if ( isset($content_struct['wp_page_parent_id']) ) 
  4665. $post_parent = $content_struct['wp_page_parent_id']; 
  4666.  
  4667. // Only set the menu_order if it was provided. 
  4668. if ( isset($content_struct['wp_page_order']) ) 
  4669. $menu_order = $content_struct['wp_page_order']; 
  4670.  
  4671. $post_author = $user->ID; 
  4672.  
  4673. // If an author id was provided then use it instead. 
  4674. if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) { 
  4675. switch ( $post_type ) { 
  4676. case "post": 
  4677. if ( !current_user_can( 'edit_others_posts' ) ) 
  4678. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); 
  4679. break; 
  4680. case "page": 
  4681. if ( !current_user_can( 'edit_others_pages' ) ) 
  4682. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create pages as this user.' ) ); 
  4683. break; 
  4684. default: 
  4685. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4686. $author = get_userdata( $content_struct['wp_author_id'] ); 
  4687. if ( ! $author ) 
  4688. return new IXR_Error( 404, __( 'Invalid author ID.' ) ); 
  4689. $post_author = $content_struct['wp_author_id']; 
  4690.  
  4691. $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null; 
  4692. $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null; 
  4693.  
  4694. $post_status = $publish ? 'publish' : 'draft'; 
  4695.  
  4696. if ( isset( $content_struct["{$post_type}_status"] ) ) { 
  4697. switch ( $content_struct["{$post_type}_status"] ) { 
  4698. case 'draft': 
  4699. case 'pending': 
  4700. case 'private': 
  4701. case 'publish': 
  4702. $post_status = $content_struct["{$post_type}_status"]; 
  4703. break; 
  4704. default: 
  4705. $post_status = $publish ? 'publish' : 'draft'; 
  4706. break; 
  4707.  
  4708. $post_excerpt = isset($content_struct['mt_excerpt']) ? $content_struct['mt_excerpt'] : null; 
  4709. $post_more = isset($content_struct['mt_text_more']) ? $content_struct['mt_text_more'] : null; 
  4710.  
  4711. $tags_input = isset($content_struct['mt_keywords']) ? $content_struct['mt_keywords'] : null; 
  4712.  
  4713. if ( isset($content_struct['mt_allow_comments']) ) { 
  4714. if ( !is_numeric($content_struct['mt_allow_comments']) ) { 
  4715. switch ( $content_struct['mt_allow_comments'] ) { 
  4716. case 'closed': 
  4717. $comment_status = 'closed'; 
  4718. break; 
  4719. case 'open': 
  4720. $comment_status = 'open'; 
  4721. break; 
  4722. default: 
  4723. $comment_status = get_default_comment_status( $post_type ); 
  4724. break; 
  4725. } else { 
  4726. switch ( (int) $content_struct['mt_allow_comments'] ) { 
  4727. case 0: 
  4728. case 2: 
  4729. $comment_status = 'closed'; 
  4730. break; 
  4731. case 1: 
  4732. $comment_status = 'open'; 
  4733. break; 
  4734. default: 
  4735. $comment_status = get_default_comment_status( $post_type ); 
  4736. break; 
  4737. } else { 
  4738. $comment_status = get_default_comment_status( $post_type ); 
  4739.  
  4740. if ( isset($content_struct['mt_allow_pings']) ) { 
  4741. if ( !is_numeric($content_struct['mt_allow_pings']) ) { 
  4742. switch ( $content_struct['mt_allow_pings'] ) { 
  4743. case 'closed': 
  4744. $ping_status = 'closed'; 
  4745. break; 
  4746. case 'open': 
  4747. $ping_status = 'open'; 
  4748. break; 
  4749. default: 
  4750. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4751. break; 
  4752. } else { 
  4753. switch ( (int) $content_struct['mt_allow_pings'] ) { 
  4754. case 0: 
  4755. $ping_status = 'closed'; 
  4756. break; 
  4757. case 1: 
  4758. $ping_status = 'open'; 
  4759. break; 
  4760. default: 
  4761. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4762. break; 
  4763. } else { 
  4764. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4765.  
  4766. if ( $post_more ) 
  4767. $post_content = $post_content . '<!--more-->' . $post_more; 
  4768.  
  4769. $to_ping = null; 
  4770. if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { 
  4771. $to_ping = $content_struct['mt_tb_ping_urls']; 
  4772. if ( is_array($to_ping) ) 
  4773. $to_ping = implode(' ', $to_ping); 
  4774.  
  4775. // Do some timestamp voodoo 
  4776. if ( !empty( $content_struct['date_created_gmt'] ) ) 
  4777. // We know this is supposed to be GMT, so we're going to slap that Z on there by force 
  4778. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; 
  4779. elseif ( !empty( $content_struct['dateCreated']) ) 
  4780. $dateCreated = $content_struct['dateCreated']->getIso(); 
  4781.  
  4782. if ( !empty( $dateCreated ) ) { 
  4783. $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 
  4784. $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); 
  4785. } else { 
  4786. $post_date = ''; 
  4787. $post_date_gmt = ''; 
  4788.  
  4789. $post_category = array(); 
  4790. if ( isset( $content_struct['categories'] ) ) { 
  4791. $catnames = $content_struct['categories']; 
  4792.  
  4793. if ( is_array($catnames) ) { 
  4794. foreach ($catnames as $cat) { 
  4795. $post_category[] = get_cat_ID($cat); 
  4796.  
  4797. $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'); 
  4798.  
  4799. $post_ID = $postdata['ID'] = get_default_post_to_edit( $post_type, true )->ID; 
  4800.  
  4801. // Only posts can be sticky 
  4802. if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 
  4803. $data = $postdata; 
  4804. $data['sticky'] = $content_struct['sticky']; 
  4805. $error = $this->_toggle_sticky( $data ); 
  4806. if ( $error ) { 
  4807. return $error; 
  4808.  
  4809. if ( isset($content_struct['custom_fields']) ) 
  4810. $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 
  4811.  
  4812. if ( isset ( $content_struct['wp_post_thumbnail'] ) ) { 
  4813. if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) 
  4814. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  4815.  
  4816. unset( $content_struct['wp_post_thumbnail'] ); 
  4817.  
  4818. // Handle enclosures 
  4819. $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null; 
  4820. $this->add_enclosure_if_new($post_ID, $thisEnclosure); 
  4821.  
  4822. $this->attach_uploads( $post_ID, $post_content ); 
  4823.  
  4824. // Handle post formats if assigned, value is validated earlier 
  4825. // in this function 
  4826. if ( isset( $content_struct['wp_post_format'] ) ) 
  4827. set_post_format( $post_ID, $content_struct['wp_post_format'] ); 
  4828.  
  4829. $post_ID = wp_insert_post( $postdata, true ); 
  4830. if ( is_wp_error( $post_ID ) ) 
  4831. return new IXR_Error(500, $post_ID->get_error_message()); 
  4832.  
  4833. if ( !$post_ID ) 
  4834. return new IXR_Error(500, __('Sorry, your entry could not be posted.')); 
  4835.  
  4836. /** 
  4837. * Fires after a new post has been successfully created via the XML-RPC MovableType API. 
  4838. * 
  4839. * @since 3.4.0 
  4840. * 
  4841. * @param int $post_ID ID of the new post. 
  4842. * @param array $args An array of arguments to create the new post. 
  4843. */ 
  4844. do_action( 'xmlrpc_call_success_mw_newPost', $post_ID, $args ); 
  4845.  
  4846. return strval($post_ID); 
  4847.  
  4848. /** 
  4849. * Adds an enclosure to a post if it's new. 
  4850. * 
  4851. * @since 2.8.0 
  4852. * 
  4853. * @param integer $post_ID Post ID. 
  4854. * @param array $enclosure Enclosure data. 
  4855. */ 
  4856. public function add_enclosure_if_new( $post_ID, $enclosure ) { 
  4857. if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { 
  4858. $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] . "\n"; 
  4859. $found = false; 
  4860. if ( $enclosures = get_post_meta( $post_ID, 'enclosure' ) ) { 
  4861. foreach ( $enclosures as $enc ) { 
  4862. // This method used to omit the trailing new line. #23219 
  4863. if ( rtrim( $enc, "\n" ) == rtrim( $encstring, "\n" ) ) { 
  4864. $found = true; 
  4865. break; 
  4866. if ( ! $found ) 
  4867. add_post_meta( $post_ID, 'enclosure', $encstring ); 
  4868.  
  4869. /** 
  4870. * Attach upload to a post. 
  4871. * 
  4872. * @since 2.1.0 
  4873. * 
  4874. * @global wpdb $wpdb WordPress database abstraction object. 
  4875. * 
  4876. * @param int $post_ID Post ID. 
  4877. * @param string $post_content Post Content for attachment. 
  4878. */ 
  4879. public function attach_uploads( $post_ID, $post_content ) { 
  4880. global $wpdb; 
  4881.  
  4882. // find any unattached files 
  4883. $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); 
  4884. if ( is_array( $attachments ) ) { 
  4885. foreach ( $attachments as $file ) { 
  4886. if ( ! empty( $file->guid ) && strpos( $post_content, $file->guid ) !== false ) 
  4887. $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) ); 
  4888.  
  4889. /** 
  4890. * Edit a post. 
  4891. * 
  4892. * @since 1.5.0 
  4893. * 
  4894. * @param array $args { 
  4895. * Method arguments. Note: arguments must be ordered as documented. 
  4896. * 
  4897. * @type int $blog_id (unused) 
  4898. * @type string $username 
  4899. * @type string $password 
  4900. * @type array $content_struct 
  4901. * @type int $publish 
  4902. * } 
  4903. * @return bool|IXR_Error True on success. 
  4904. */ 
  4905. public function mw_editPost( $args ) { 
  4906. $this->escape( $args ); 
  4907.  
  4908. $post_ID = (int) $args[0]; 
  4909. $username = $args[1]; 
  4910. $password = $args[2]; 
  4911. $content_struct = $args[3]; 
  4912. $publish = isset( $args[4] ) ? $args[4] : 0; 
  4913.  
  4914. if ( ! $user = $this->login($username, $password) ) 
  4915. return $this->error; 
  4916.  
  4917. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4918. do_action( 'xmlrpc_call', 'metaWeblog.editPost' ); 
  4919.  
  4920. $postdata = get_post( $post_ID, ARRAY_A ); 
  4921.  
  4922. /** 
  4923. * If there is no post data for the give post id, stop now and return an error. 
  4924. * Otherwise a new post will be created (which was the old behavior). 
  4925. */ 
  4926. if ( ! $postdata || empty( $postdata[ 'ID' ] ) ) 
  4927. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4928.  
  4929. if ( ! current_user_can( 'edit_post', $post_ID ) ) 
  4930. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  4931.  
  4932. // Use wp.editPost to edit post types other than post and page. 
  4933. if ( ! in_array( $postdata[ 'post_type' ], array( 'post', 'page' ) ) ) 
  4934. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4935.  
  4936. // Thwart attempt to change the post type. 
  4937. if ( ! empty( $content_struct[ 'post_type' ] ) && ( $content_struct['post_type'] != $postdata[ 'post_type' ] ) ) 
  4938. return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); 
  4939.  
  4940. // Check for a valid post format if one was given 
  4941. if ( isset( $content_struct['wp_post_format'] ) ) { 
  4942. $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); 
  4943. if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { 
  4944. return new IXR_Error( 404, __( 'Invalid post format.' ) ); 
  4945.  
  4946. $this->escape($postdata); 
  4947.  
  4948. $ID = $postdata['ID']; 
  4949. $post_content = $postdata['post_content']; 
  4950. $post_title = $postdata['post_title']; 
  4951. $post_excerpt = $postdata['post_excerpt']; 
  4952. $post_password = $postdata['post_password']; 
  4953. $post_parent = $postdata['post_parent']; 
  4954. $post_type = $postdata['post_type']; 
  4955. $menu_order = $postdata['menu_order']; 
  4956.  
  4957. // Let WordPress manage slug if none was provided. 
  4958. $post_name = $postdata['post_name']; 
  4959. if ( isset($content_struct['wp_slug']) ) 
  4960. $post_name = $content_struct['wp_slug']; 
  4961.  
  4962. // Only use a password if one was given. 
  4963. if ( isset($content_struct['wp_password']) ) 
  4964. $post_password = $content_struct['wp_password']; 
  4965.  
  4966. // Only set a post parent if one was given. 
  4967. if ( isset($content_struct['wp_page_parent_id']) ) 
  4968. $post_parent = $content_struct['wp_page_parent_id']; 
  4969.  
  4970. // Only set the menu_order if it was given. 
  4971. if ( isset($content_struct['wp_page_order']) ) 
  4972. $menu_order = $content_struct['wp_page_order']; 
  4973.  
  4974. $page_template = null; 
  4975. if ( ! empty( $content_struct['wp_page_template'] ) && 'page' == $post_type ) 
  4976. $page_template = $content_struct['wp_page_template']; 
  4977.  
  4978. $post_author = $postdata['post_author']; 
  4979.  
  4980. // Only set the post_author if one is set. 
  4981. if ( isset( $content_struct['wp_author_id'] ) ) { 
  4982. // Check permissions if attempting to switch author to or from another user. 
  4983. if ( $user->ID != $content_struct['wp_author_id'] || $user->ID != $post_author ) { 
  4984. switch ( $post_type ) { 
  4985. case 'post': 
  4986. if ( ! current_user_can( 'edit_others_posts' ) ) { 
  4987. return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the post author as this user.' ) ); 
  4988. break; 
  4989. case 'page': 
  4990. if ( ! current_user_can( 'edit_others_pages' ) ) { 
  4991. return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the page author as this user.' ) ); 
  4992. break; 
  4993. default: 
  4994. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4995. $post_author = $content_struct['wp_author_id']; 
  4996.  
  4997. if ( isset($content_struct['mt_allow_comments']) ) { 
  4998. if ( !is_numeric($content_struct['mt_allow_comments']) ) { 
  4999. switch ( $content_struct['mt_allow_comments'] ) { 
  5000. case 'closed': 
  5001. $comment_status = 'closed'; 
  5002. break; 
  5003. case 'open': 
  5004. $comment_status = 'open'; 
  5005. break; 
  5006. default: 
  5007. $comment_status = get_default_comment_status( $post_type ); 
  5008. break; 
  5009. } else { 
  5010. switch ( (int) $content_struct['mt_allow_comments'] ) { 
  5011. case 0: 
  5012. case 2: 
  5013. $comment_status = 'closed'; 
  5014. break; 
  5015. case 1: 
  5016. $comment_status = 'open'; 
  5017. break; 
  5018. default: 
  5019. $comment_status = get_default_comment_status( $post_type ); 
  5020. break; 
  5021.  
  5022. if ( isset($content_struct['mt_allow_pings']) ) { 
  5023. if ( !is_numeric($content_struct['mt_allow_pings']) ) { 
  5024. switch ( $content_struct['mt_allow_pings'] ) { 
  5025. case 'closed': 
  5026. $ping_status = 'closed'; 
  5027. break; 
  5028. case 'open': 
  5029. $ping_status = 'open'; 
  5030. break; 
  5031. default: 
  5032. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  5033. break; 
  5034. } else { 
  5035. switch ( (int) $content_struct["mt_allow_pings"] ) { 
  5036. case 0: 
  5037. $ping_status = 'closed'; 
  5038. break; 
  5039. case 1: 
  5040. $ping_status = 'open'; 
  5041. break; 
  5042. default: 
  5043. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  5044. break; 
  5045.  
  5046. if ( isset( $content_struct['title'] ) ) 
  5047. $post_title = $content_struct['title']; 
  5048.  
  5049. if ( isset( $content_struct['description'] ) ) 
  5050. $post_content = $content_struct['description']; 
  5051.  
  5052. $post_category = array(); 
  5053. if ( isset( $content_struct['categories'] ) ) { 
  5054. $catnames = $content_struct['categories']; 
  5055. if ( is_array($catnames) ) { 
  5056. foreach ($catnames as $cat) { 
  5057. $post_category[] = get_cat_ID($cat); 
  5058.  
  5059. if ( isset( $content_struct['mt_excerpt'] ) ) 
  5060. $post_excerpt = $content_struct['mt_excerpt']; 
  5061.  
  5062. $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null; 
  5063.  
  5064. $post_status = $publish ? 'publish' : 'draft'; 
  5065. if ( isset( $content_struct["{$post_type}_status"] ) ) { 
  5066. switch( $content_struct["{$post_type}_status"] ) { 
  5067. case 'draft': 
  5068. case 'pending': 
  5069. case 'private': 
  5070. case 'publish': 
  5071. $post_status = $content_struct["{$post_type}_status"]; 
  5072. break; 
  5073. default: 
  5074. $post_status = $publish ? 'publish' : 'draft'; 
  5075. break; 
  5076.  
  5077. $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null; 
  5078.  
  5079. if ( 'publish' == $post_status || 'private' == $post_status ) { 
  5080. if ( 'page' == $post_type && ! current_user_can( 'publish_pages' ) ) { 
  5081. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this page.' ) ); 
  5082. } elseif ( ! current_user_can( 'publish_posts' ) ) { 
  5083. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); 
  5084.  
  5085. if ( $post_more ) 
  5086. $post_content = $post_content . "<!--more-->" . $post_more; 
  5087.  
  5088. $to_ping = null; 
  5089. if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { 
  5090. $to_ping = $content_struct['mt_tb_ping_urls']; 
  5091. if ( is_array($to_ping) ) 
  5092. $to_ping = implode(' ', $to_ping); 
  5093.  
  5094. // Do some timestamp voodoo. 
  5095. if ( !empty( $content_struct['date_created_gmt'] ) ) 
  5096. // We know this is supposed to be GMT, so we're going to slap that Z on there by force. 
  5097. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; 
  5098. elseif ( !empty( $content_struct['dateCreated']) ) 
  5099. $dateCreated = $content_struct['dateCreated']->getIso(); 
  5100.  
  5101. // Default to not flagging the post date to be edited unless it's intentional. 
  5102. $edit_date = false; 
  5103.  
  5104. if ( !empty( $dateCreated ) ) { 
  5105. $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 
  5106. $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); 
  5107.  
  5108. // Flag the post date to be edited. 
  5109. $edit_date = true; 
  5110. } else { 
  5111. $post_date = $postdata['post_date']; 
  5112. $post_date_gmt = $postdata['post_date_gmt']; 
  5113.  
  5114. // We've got all the data -- post it. 
  5115. $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'edit_date', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template'); 
  5116.  
  5117. $result = wp_update_post($newpost, true); 
  5118. if ( is_wp_error( $result ) ) 
  5119. return new IXR_Error(500, $result->get_error_message()); 
  5120.  
  5121. if ( !$result ) 
  5122. return new IXR_Error(500, __('Sorry, your entry could not be edited.')); 
  5123.  
  5124. // Only posts can be sticky 
  5125. if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 
  5126. $data = $newpost; 
  5127. $data['sticky'] = $content_struct['sticky']; 
  5128. $data['post_type'] = 'post'; 
  5129. $error = $this->_toggle_sticky( $data, true ); 
  5130. if ( $error ) { 
  5131. return $error; 
  5132.  
  5133. if ( isset($content_struct['custom_fields']) ) 
  5134. $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 
  5135.  
  5136. if ( isset ( $content_struct['wp_post_thumbnail'] ) ) { 
  5137.  
  5138. // Empty value deletes, non-empty value adds/updates. 
  5139. if ( empty( $content_struct['wp_post_thumbnail'] ) ) { 
  5140. delete_post_thumbnail( $post_ID ); 
  5141. } else { 
  5142. if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) 
  5143. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  5144. unset( $content_struct['wp_post_thumbnail'] ); 
  5145.  
  5146. // Handle enclosures. 
  5147. $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null; 
  5148. $this->add_enclosure_if_new($post_ID, $thisEnclosure); 
  5149.  
  5150. $this->attach_uploads( $ID, $post_content ); 
  5151.  
  5152. // Handle post formats if assigned, validation is handled earlier in this function. 
  5153. if ( isset( $content_struct['wp_post_format'] ) ) 
  5154. set_post_format( $post_ID, $content_struct['wp_post_format'] ); 
  5155.  
  5156. /** 
  5157. * Fires after a post has been successfully updated via the XML-RPC MovableType API. 
  5158. * 
  5159. * @since 3.4.0 
  5160. * 
  5161. * @param int $post_ID ID of the updated post. 
  5162. * @param array $args An array of arguments to update the post. 
  5163. */ 
  5164. do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args ); 
  5165.  
  5166. return true; 
  5167.  
  5168. /** 
  5169. * Retrieve post. 
  5170. * 
  5171. * @since 1.5.0 
  5172. * 
  5173. * @param array $args { 
  5174. * Method arguments. Note: arguments must be ordered as documented. 
  5175. * 
  5176. * @type int $blog_id (unused) 
  5177. * @type int $post_ID 
  5178. * @type string $username 
  5179. * @type string $password 
  5180. * } 
  5181. * @return array|IXR_Error 
  5182. */ 
  5183. public function mw_getPost( $args ) { 
  5184. $this->escape( $args ); 
  5185.  
  5186. $post_ID = (int) $args[0]; 
  5187. $username = $args[1]; 
  5188. $password = $args[2]; 
  5189.  
  5190. if ( !$user = $this->login($username, $password) ) 
  5191. return $this->error; 
  5192.  
  5193. $postdata = get_post($post_ID, ARRAY_A); 
  5194. if ( ! $postdata ) 
  5195. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5196.  
  5197. if ( !current_user_can( 'edit_post', $post_ID ) ) 
  5198. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  5199.  
  5200. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5201. do_action( 'xmlrpc_call', 'metaWeblog.getPost' ); 
  5202.  
  5203. if ($postdata['post_date'] != '') { 
  5204. $post_date = $this->_convert_date( $postdata['post_date'] ); 
  5205. $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] ); 
  5206. $post_modified = $this->_convert_date( $postdata['post_modified'] ); 
  5207. $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] ); 
  5208.  
  5209. $categories = array(); 
  5210. $catids = wp_get_post_categories($post_ID); 
  5211. foreach ($catids as $catid) 
  5212. $categories[] = get_cat_name($catid); 
  5213.  
  5214. $tagnames = array(); 
  5215. $tags = wp_get_post_tags( $post_ID ); 
  5216. if ( !empty( $tags ) ) { 
  5217. foreach ( $tags as $tag ) 
  5218. $tagnames[] = $tag->name; 
  5219. $tagnames = implode( ', ', $tagnames ); 
  5220. } else { 
  5221. $tagnames = ''; 
  5222.  
  5223. $post = get_extended($postdata['post_content']); 
  5224. $link = get_permalink($postdata['ID']); 
  5225.  
  5226. // Get the author info. 
  5227. $author = get_userdata($postdata['post_author']); 
  5228.  
  5229. $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; 
  5230. $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; 
  5231.  
  5232. // Consider future posts as published 
  5233. if ( $postdata['post_status'] === 'future' ) 
  5234. $postdata['post_status'] = 'publish'; 
  5235.  
  5236. // Get post format 
  5237. $post_format = get_post_format( $post_ID ); 
  5238. if ( empty( $post_format ) ) 
  5239. $post_format = 'standard'; 
  5240.  
  5241. $sticky = false; 
  5242. if ( is_sticky( $post_ID ) ) 
  5243. $sticky = true; 
  5244.  
  5245. $enclosure = array(); 
  5246. foreach ( (array) get_post_custom($post_ID) as $key => $val) { 
  5247. if ($key == 'enclosure') { 
  5248. foreach ( (array) $val as $enc ) { 
  5249. $encdata = explode("\n", $enc); 
  5250. $enclosure['url'] = trim(htmlspecialchars($encdata[0])); 
  5251. $enclosure['length'] = (int) trim($encdata[1]); 
  5252. $enclosure['type'] = trim($encdata[2]); 
  5253. break 2; 
  5254.  
  5255. $resp = array( 
  5256. 'dateCreated' => $post_date,  
  5257. 'userid' => $postdata['post_author'],  
  5258. 'postid' => $postdata['ID'],  
  5259. 'description' => $post['main'],  
  5260. 'title' => $postdata['post_title'],  
  5261. 'link' => $link,  
  5262. 'permaLink' => $link,  
  5263. // commented out because no other tool seems to use this 
  5264. // 'content' => $entry['post_content'],  
  5265. 'categories' => $categories,  
  5266. 'mt_excerpt' => $postdata['post_excerpt'],  
  5267. 'mt_text_more' => $post['extended'],  
  5268. 'wp_more_text' => $post['more_text'],  
  5269. 'mt_allow_comments' => $allow_comments,  
  5270. 'mt_allow_pings' => $allow_pings,  
  5271. 'mt_keywords' => $tagnames,  
  5272. 'wp_slug' => $postdata['post_name'],  
  5273. 'wp_password' => $postdata['post_password'],  
  5274. 'wp_author_id' => (string) $author->ID,  
  5275. 'wp_author_display_name' => $author->display_name,  
  5276. 'date_created_gmt' => $post_date_gmt,  
  5277. 'post_status' => $postdata['post_status'],  
  5278. 'custom_fields' => $this->get_custom_fields($post_ID),  
  5279. 'wp_post_format' => $post_format,  
  5280. 'sticky' => $sticky,  
  5281. 'date_modified' => $post_modified,  
  5282. 'date_modified_gmt' => $post_modified_gmt 
  5283. ); 
  5284.  
  5285. if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure; 
  5286.  
  5287. $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] ); 
  5288.  
  5289. return $resp; 
  5290. } else { 
  5291. return new IXR_Error(404, __('Sorry, no such post.')); 
  5292.  
  5293. /** 
  5294. * Retrieve list of recent posts. 
  5295. * 
  5296. * @since 1.5.0 
  5297. * 
  5298. * @param array $args { 
  5299. * Method arguments. Note: arguments must be ordered as documented. 
  5300. * 
  5301. * @type int $blog_id (unused) 
  5302. * @type string $username 
  5303. * @type string $password 
  5304. * @type int $numberposts 
  5305. * } 
  5306. * @return array|IXR_Error 
  5307. */ 
  5308. public function mw_getRecentPosts( $args ) { 
  5309. $this->escape( $args ); 
  5310.  
  5311. $username = $args[1]; 
  5312. $password = $args[2]; 
  5313. if ( isset( $args[3] ) ) 
  5314. $query = array( 'numberposts' => absint( $args[3] ) ); 
  5315. else 
  5316. $query = array(); 
  5317.  
  5318. if ( !$user = $this->login($username, $password) ) 
  5319. return $this->error; 
  5320.  
  5321. if ( ! current_user_can( 'edit_posts' ) ) 
  5322. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  5323.  
  5324. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5325. do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts' ); 
  5326.  
  5327. $posts_list = wp_get_recent_posts( $query ); 
  5328.  
  5329. if ( !$posts_list ) 
  5330. return array(); 
  5331.  
  5332. $recent_posts = array(); 
  5333. foreach ($posts_list as $entry) { 
  5334. if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 
  5335. continue; 
  5336.  
  5337. $post_date = $this->_convert_date( $entry['post_date'] ); 
  5338. $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); 
  5339. $post_modified = $this->_convert_date( $entry['post_modified'] ); 
  5340. $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] ); 
  5341.  
  5342. $categories = array(); 
  5343. $catids = wp_get_post_categories($entry['ID']); 
  5344. foreach ( $catids as $catid ) 
  5345. $categories[] = get_cat_name($catid); 
  5346.  
  5347. $tagnames = array(); 
  5348. $tags = wp_get_post_tags( $entry['ID'] ); 
  5349. if ( !empty( $tags ) ) { 
  5350. foreach ( $tags as $tag ) { 
  5351. $tagnames[] = $tag->name; 
  5352. $tagnames = implode( ', ', $tagnames ); 
  5353. } else { 
  5354. $tagnames = ''; 
  5355.  
  5356. $post = get_extended($entry['post_content']); 
  5357. $link = get_permalink($entry['ID']); 
  5358.  
  5359. // Get the post author info. 
  5360. $author = get_userdata($entry['post_author']); 
  5361.  
  5362. $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; 
  5363. $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; 
  5364.  
  5365. // Consider future posts as published 
  5366. if ( $entry['post_status'] === 'future' ) 
  5367. $entry['post_status'] = 'publish'; 
  5368.  
  5369. // Get post format 
  5370. $post_format = get_post_format( $entry['ID'] ); 
  5371. if ( empty( $post_format ) ) 
  5372. $post_format = 'standard'; 
  5373.  
  5374. $recent_posts[] = array( 
  5375. 'dateCreated' => $post_date,  
  5376. 'userid' => $entry['post_author'],  
  5377. 'postid' => (string) $entry['ID'],  
  5378. 'description' => $post['main'],  
  5379. 'title' => $entry['post_title'],  
  5380. 'link' => $link,  
  5381. 'permaLink' => $link,  
  5382. // commented out because no other tool seems to use this 
  5383. // 'content' => $entry['post_content'],  
  5384. 'categories' => $categories,  
  5385. 'mt_excerpt' => $entry['post_excerpt'],  
  5386. 'mt_text_more' => $post['extended'],  
  5387. 'wp_more_text' => $post['more_text'],  
  5388. 'mt_allow_comments' => $allow_comments,  
  5389. 'mt_allow_pings' => $allow_pings,  
  5390. 'mt_keywords' => $tagnames,  
  5391. 'wp_slug' => $entry['post_name'],  
  5392. 'wp_password' => $entry['post_password'],  
  5393. 'wp_author_id' => (string) $author->ID,  
  5394. 'wp_author_display_name' => $author->display_name,  
  5395. 'date_created_gmt' => $post_date_gmt,  
  5396. 'post_status' => $entry['post_status'],  
  5397. 'custom_fields' => $this->get_custom_fields($entry['ID']),  
  5398. 'wp_post_format' => $post_format,  
  5399. 'date_modified' => $post_modified,  
  5400. 'date_modified_gmt' => $post_modified_gmt,  
  5401. 'sticky' => ( $entry['post_type'] === 'post' && is_sticky( $entry['ID'] ) ),  
  5402. 'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ) 
  5403. ); 
  5404.  
  5405. return $recent_posts; 
  5406.  
  5407. /** 
  5408. * Retrieve the list of categories on a given blog. 
  5409. * 
  5410. * @since 1.5.0 
  5411. * 
  5412. * @param array $args { 
  5413. * Method arguments. Note: arguments must be ordered as documented. 
  5414. * 
  5415. * @type int $blog_id (unused) 
  5416. * @type string $username 
  5417. * @type string $password 
  5418. * } 
  5419. * @return array|IXR_Error 
  5420. */ 
  5421. public function mw_getCategories( $args ) { 
  5422. $this->escape( $args ); 
  5423.  
  5424. $username = $args[1]; 
  5425. $password = $args[2]; 
  5426.  
  5427. if ( !$user = $this->login($username, $password) ) 
  5428. return $this->error; 
  5429.  
  5430. if ( !current_user_can( 'edit_posts' ) ) 
  5431. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 
  5432.  
  5433. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5434. do_action( 'xmlrpc_call', 'metaWeblog.getCategories' ); 
  5435.  
  5436. $categories_struct = array(); 
  5437.  
  5438. if ( $cats = get_categories(array('get' => 'all')) ) { 
  5439. foreach ( $cats as $cat ) { 
  5440. $struct = array(); 
  5441. $struct['categoryId'] = $cat->term_id; 
  5442. $struct['parentId'] = $cat->parent; 
  5443. $struct['description'] = $cat->name; 
  5444. $struct['categoryDescription'] = $cat->description; 
  5445. $struct['categoryName'] = $cat->name; 
  5446. $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id)); 
  5447. $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2')); 
  5448.  
  5449. $categories_struct[] = $struct; 
  5450.  
  5451. return $categories_struct; 
  5452.  
  5453. /** 
  5454. * Uploads a file, following your settings. 
  5455. * 
  5456. * Adapted from a patch by Johann Richard. 
  5457. * 
  5458. * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ 
  5459. * 
  5460. * @since 1.5.0 
  5461. * 
  5462. * @global wpdb $wpdb WordPress database abstraction object. 
  5463. * 
  5464. * @param array $args { 
  5465. * Method arguments. Note: arguments must be ordered as documented. 
  5466. * 
  5467. * @type int $blog_id (unused) 
  5468. * @type string $username 
  5469. * @type string $password 
  5470. * @type array $data 
  5471. * } 
  5472. * @return array|IXR_Error 
  5473. */ 
  5474. public function mw_newMediaObject( $args ) { 
  5475. global $wpdb; 
  5476.  
  5477. $username = $this->escape( $args[1] ); 
  5478. $password = $this->escape( $args[2] ); 
  5479. $data = $args[3]; 
  5480.  
  5481. $name = sanitize_file_name( $data['name'] ); 
  5482. $type = $data['type']; 
  5483. $bits = $data['bits']; 
  5484.  
  5485. if ( !$user = $this->login($username, $password) ) 
  5486. return $this->error; 
  5487.  
  5488. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5489. do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject' ); 
  5490.  
  5491. if ( !current_user_can('upload_files') ) { 
  5492. $this->error = new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); 
  5493. return $this->error; 
  5494.  
  5495. if ( is_multisite() && upload_is_user_over_quota( false ) ) { 
  5496. $this->error = new IXR_Error( 401, __( 'Sorry, you have used your space allocation.' ) ); 
  5497. return $this->error; 
  5498.  
  5499. /** 
  5500. * Filters whether to preempt the XML-RPC media upload. 
  5501. * 
  5502. * Passing a truthy value will effectively short-circuit the media upload,  
  5503. * returning that value as a 500 error instead. 
  5504. * 
  5505. * @since 2.1.0 
  5506. * 
  5507. * @param bool $error Whether to pre-empt the media upload. Default false. 
  5508. */ 
  5509. if ( $upload_err = apply_filters( 'pre_upload_error', false ) ) { 
  5510. return new IXR_Error( 500, $upload_err ); 
  5511.  
  5512. $upload = wp_upload_bits($name, null, $bits); 
  5513. if ( ! empty($upload['error']) ) { 
  5514. /** translators: 1: file name, 2: error message */ 
  5515. $errorString = sprintf( __( 'Could not write file %1$s (%2$s).' ), $name, $upload['error'] ); 
  5516. return new IXR_Error( 500, $errorString ); 
  5517. // Construct the attachment array 
  5518. $post_id = 0; 
  5519. if ( ! empty( $data['post_id'] ) ) { 
  5520. $post_id = (int) $data['post_id']; 
  5521.  
  5522. if ( ! current_user_can( 'edit_post', $post_id ) ) 
  5523. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  5524. $attachment = array( 
  5525. 'post_title' => $name,  
  5526. 'post_content' => '',  
  5527. 'post_type' => 'attachment',  
  5528. 'post_parent' => $post_id,  
  5529. 'post_mime_type' => $type,  
  5530. 'guid' => $upload[ 'url' ] 
  5531. ); 
  5532.  
  5533. // Save the data 
  5534. $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); 
  5535. wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 
  5536.  
  5537. /** 
  5538. * Fires after a new attachment has been added via the XML-RPC MovableType API. 
  5539. * 
  5540. * @since 3.4.0 
  5541. * 
  5542. * @param int $id ID of the new attachment. 
  5543. * @param array $args An array of arguments to add the attachment. 
  5544. */ 
  5545. do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args ); 
  5546.  
  5547. $struct = $this->_prepare_media_item( get_post( $id ) ); 
  5548.  
  5549. // Deprecated values 
  5550. $struct['id'] = $struct['attachment_id']; 
  5551. $struct['file'] = $struct['title']; 
  5552. $struct['url'] = $struct['link']; 
  5553.  
  5554. return $struct; 
  5555.  
  5556. /** MovableType API functions 
  5557. * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html 
  5558. */ 
  5559.  
  5560. /** 
  5561. * Retrieve the post titles of recent posts. 
  5562. * 
  5563. * @since 1.5.0 
  5564. * 
  5565. * @param array $args { 
  5566. * Method arguments. Note: arguments must be ordered as documented. 
  5567. * 
  5568. * @type int $blog_id (unused) 
  5569. * @type string $username 
  5570. * @type string $password 
  5571. * @type int $numberposts 
  5572. * } 
  5573. * @return array|IXR_Error 
  5574. */ 
  5575. public function mt_getRecentPostTitles( $args ) { 
  5576. $this->escape( $args ); 
  5577.  
  5578. $username = $args[1]; 
  5579. $password = $args[2]; 
  5580. if ( isset( $args[3] ) ) 
  5581. $query = array( 'numberposts' => absint( $args[3] ) ); 
  5582. else 
  5583. $query = array(); 
  5584.  
  5585. if ( !$user = $this->login($username, $password) ) 
  5586. return $this->error; 
  5587.  
  5588. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5589. do_action( 'xmlrpc_call', 'mt.getRecentPostTitles' ); 
  5590.  
  5591. $posts_list = wp_get_recent_posts( $query ); 
  5592.  
  5593. if ( !$posts_list ) { 
  5594. $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 
  5595. return $this->error; 
  5596.  
  5597. $recent_posts = array(); 
  5598.  
  5599. foreach ($posts_list as $entry) { 
  5600. if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 
  5601. continue; 
  5602.  
  5603. $post_date = $this->_convert_date( $entry['post_date'] ); 
  5604. $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); 
  5605.  
  5606. $recent_posts[] = array( 
  5607. 'dateCreated' => $post_date,  
  5608. 'userid' => $entry['post_author'],  
  5609. 'postid' => (string) $entry['ID'],  
  5610. 'title' => $entry['post_title'],  
  5611. 'post_status' => $entry['post_status'],  
  5612. 'date_created_gmt' => $post_date_gmt 
  5613. ); 
  5614.  
  5615. return $recent_posts; 
  5616.  
  5617. /** 
  5618. * Retrieve list of all categories on blog. 
  5619. * 
  5620. * @since 1.5.0 
  5621. * 
  5622. * @param array $args { 
  5623. * Method arguments. Note: arguments must be ordered as documented. 
  5624. * 
  5625. * @type int $blog_id (unused) 
  5626. * @type string $username 
  5627. * @type string $password 
  5628. * } 
  5629. * @return array|IXR_Error 
  5630. */ 
  5631. public function mt_getCategoryList( $args ) { 
  5632. $this->escape( $args ); 
  5633.  
  5634. $username = $args[1]; 
  5635. $password = $args[2]; 
  5636.  
  5637. if ( !$user = $this->login($username, $password) ) 
  5638. return $this->error; 
  5639.  
  5640. if ( !current_user_can( 'edit_posts' ) ) 
  5641. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 
  5642.  
  5643. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5644. do_action( 'xmlrpc_call', 'mt.getCategoryList' ); 
  5645.  
  5646. $categories_struct = array(); 
  5647.  
  5648. if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) { 
  5649. foreach ( $cats as $cat ) { 
  5650. $struct = array(); 
  5651. $struct['categoryId'] = $cat->term_id; 
  5652. $struct['categoryName'] = $cat->name; 
  5653.  
  5654. $categories_struct[] = $struct; 
  5655.  
  5656. return $categories_struct; 
  5657.  
  5658. /** 
  5659. * Retrieve post categories. 
  5660. * 
  5661. * @since 1.5.0 
  5662. * 
  5663. * @param array $args { 
  5664. * Method arguments. Note: arguments must be ordered as documented. 
  5665. * 
  5666. * @type int $post_ID 
  5667. * @type string $username 
  5668. * @type string $password 
  5669. * } 
  5670. * @return array|IXR_Error 
  5671. */ 
  5672. public function mt_getPostCategories( $args ) { 
  5673. $this->escape( $args ); 
  5674.  
  5675. $post_ID = (int) $args[0]; 
  5676. $username = $args[1]; 
  5677. $password = $args[2]; 
  5678.  
  5679. if ( !$user = $this->login($username, $password) ) 
  5680. return $this->error; 
  5681.  
  5682. if ( ! get_post( $post_ID ) ) 
  5683. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5684.  
  5685. if ( !current_user_can( 'edit_post', $post_ID ) ) 
  5686. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  5687.  
  5688. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5689. do_action( 'xmlrpc_call', 'mt.getPostCategories' ); 
  5690.  
  5691. $categories = array(); 
  5692. $catids = wp_get_post_categories(intval($post_ID)); 
  5693. // first listed category will be the primary category 
  5694. $isPrimary = true; 
  5695. foreach ( $catids as $catid ) { 
  5696. $categories[] = array( 
  5697. 'categoryName' => get_cat_name($catid),  
  5698. 'categoryId' => (string) $catid,  
  5699. 'isPrimary' => $isPrimary 
  5700. ); 
  5701. $isPrimary = false; 
  5702.  
  5703. return $categories; 
  5704.  
  5705. /** 
  5706. * Sets categories for a post. 
  5707. * 
  5708. * @since 1.5.0 
  5709. * 
  5710. * @param array $args { 
  5711. * Method arguments. Note: arguments must be ordered as documented. 
  5712. * 
  5713. * @type int $post_ID 
  5714. * @type string $username 
  5715. * @type string $password 
  5716. * @type array $categories 
  5717. * } 
  5718. * @return true|IXR_Error True on success. 
  5719. */ 
  5720. public function mt_setPostCategories( $args ) { 
  5721. $this->escape( $args ); 
  5722.  
  5723. $post_ID = (int) $args[0]; 
  5724. $username = $args[1]; 
  5725. $password = $args[2]; 
  5726. $categories = $args[3]; 
  5727.  
  5728. if ( !$user = $this->login($username, $password) ) 
  5729. return $this->error; 
  5730.  
  5731. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5732. do_action( 'xmlrpc_call', 'mt.setPostCategories' ); 
  5733.  
  5734. if ( ! get_post( $post_ID ) ) 
  5735. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5736.  
  5737. if ( !current_user_can('edit_post', $post_ID) ) 
  5738. return new IXR_Error(401, __('Sorry, you are not allowed to edit this post.')); 
  5739.  
  5740. $catids = array(); 
  5741. foreach ( $categories as $cat ) { 
  5742. $catids[] = $cat['categoryId']; 
  5743.  
  5744. wp_set_post_categories($post_ID, $catids); 
  5745.  
  5746. return true; 
  5747.  
  5748. /** 
  5749. * Retrieve an array of methods supported by this server. 
  5750. * 
  5751. * @since 1.5.0 
  5752. * 
  5753. * @return array 
  5754. */ 
  5755. public function mt_supportedMethods() { 
  5756. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5757. do_action( 'xmlrpc_call', 'mt.supportedMethods' ); 
  5758.  
  5759. return array_keys( $this->methods ); 
  5760.  
  5761. /** 
  5762. * Retrieve an empty array because we don't support per-post text filters. 
  5763. * 
  5764. * @since 1.5.0 
  5765. */ 
  5766. public function mt_supportedTextFilters() { 
  5767. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5768. do_action( 'xmlrpc_call', 'mt.supportedTextFilters' ); 
  5769.  
  5770. /** 
  5771. * Filters the MoveableType text filters list for XML-RPC. 
  5772. * 
  5773. * @since 2.2.0 
  5774. * 
  5775. * @param array $filters An array of text filters. 
  5776. */ 
  5777. return apply_filters( 'xmlrpc_text_filters', array() ); 
  5778.  
  5779. /** 
  5780. * Retrieve trackbacks sent to a given post. 
  5781. * 
  5782. * @since 1.5.0 
  5783. * 
  5784. * @global wpdb $wpdb WordPress database abstraction object. 
  5785. * 
  5786. * @param int $post_ID 
  5787. * @return array|IXR_Error 
  5788. */ 
  5789. public function mt_getTrackbackPings( $post_ID ) { 
  5790. global $wpdb; 
  5791.  
  5792. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5793. do_action( 'xmlrpc_call', 'mt.getTrackbackPings' ); 
  5794.  
  5795. $actual_post = get_post($post_ID, ARRAY_A); 
  5796.  
  5797. if ( !$actual_post ) 
  5798. return new IXR_Error(404, __('Sorry, no such post.')); 
  5799.  
  5800. $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) ); 
  5801.  
  5802. if ( !$comments ) 
  5803. return array(); 
  5804.  
  5805. $trackback_pings = array(); 
  5806. foreach ( $comments as $comment ) { 
  5807. if ( 'trackback' == $comment->comment_type ) { 
  5808. $content = $comment->comment_content; 
  5809. $title = substr($content, 8, (strpos($content, '</strong>') - 8)); 
  5810. $trackback_pings[] = array( 
  5811. 'pingTitle' => $title,  
  5812. 'pingURL' => $comment->comment_author_url,  
  5813. 'pingIP' => $comment->comment_author_IP 
  5814. ); 
  5815.  
  5816. return $trackback_pings; 
  5817.  
  5818. /** 
  5819. * Sets a post's publish status to 'publish'. 
  5820. * 
  5821. * @since 1.5.0 
  5822. * 
  5823. * @param array $args { 
  5824. * Method arguments. Note: arguments must be ordered as documented. 
  5825. * 
  5826. * @type int $post_ID 
  5827. * @type string $username 
  5828. * @type string $password 
  5829. * } 
  5830. * @return int|IXR_Error 
  5831. */ 
  5832. public function mt_publishPost( $args ) { 
  5833. $this->escape( $args ); 
  5834.  
  5835. $post_ID = (int) $args[0]; 
  5836. $username = $args[1]; 
  5837. $password = $args[2]; 
  5838.  
  5839. if ( !$user = $this->login($username, $password) ) 
  5840. return $this->error; 
  5841.  
  5842. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5843. do_action( 'xmlrpc_call', 'mt.publishPost' ); 
  5844.  
  5845. $postdata = get_post($post_ID, ARRAY_A); 
  5846. if ( ! $postdata ) 
  5847. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5848.  
  5849. if ( !current_user_can('publish_posts') || !current_user_can('edit_post', $post_ID) ) 
  5850. return new IXR_Error(401, __('Sorry, you are not allowed to publish this post.')); 
  5851.  
  5852. $postdata['post_status'] = 'publish'; 
  5853.  
  5854. // retain old cats 
  5855. $cats = wp_get_post_categories($post_ID); 
  5856. $postdata['post_category'] = $cats; 
  5857. $this->escape($postdata); 
  5858.  
  5859. return wp_update_post( $postdata ); 
  5860.  
  5861. /** PingBack functions 
  5862. * specs on www.hixie.ch/specs/pingback/pingback 
  5863. */ 
  5864.  
  5865. /** 
  5866. * Retrieves a pingback and registers it. 
  5867. * 
  5868. * @since 1.5.0 
  5869. * 
  5870. * @param array $args { 
  5871. * Method arguments. Note: arguments must be ordered as documented. 
  5872. * 
  5873. * @type string $pagelinkedfrom 
  5874. * @type string $pagelinkedto 
  5875. * } 
  5876. * @return string|IXR_Error 
  5877. */ 
  5878. public function pingback_ping( $args ) { 
  5879. global $wpdb; 
  5880.  
  5881. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5882. do_action( 'xmlrpc_call', 'pingback.ping' ); 
  5883.  
  5884. $this->escape( $args ); 
  5885.  
  5886. $pagelinkedfrom = str_replace( '&', '&', $args[0] ); 
  5887. $pagelinkedto = str_replace( '&', '&', $args[1] ); 
  5888. $pagelinkedto = str_replace( '&', '&', $pagelinkedto ); 
  5889.  
  5890. /** 
  5891. * Filters the pingback source URI. 
  5892. * 
  5893. * @since 3.6.0 
  5894. * 
  5895. * @param string $pagelinkedfrom URI of the page linked from. 
  5896. * @param string $pagelinkedto URI of the page linked to. 
  5897. */ 
  5898. $pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto ); 
  5899.  
  5900. if ( ! $pagelinkedfrom ) 
  5901. return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) ); 
  5902.  
  5903. // Check if the page linked to is in our site 
  5904. $pos1 = strpos($pagelinkedto, str_replace(array('http://www.', 'http://', 'https://www.', 'https://'), '', get_option('home'))); 
  5905. if ( !$pos1 ) 
  5906. return $this->pingback_error( 0, __( 'Is there no link to us?' ) ); 
  5907.  
  5908. // let's find which post is linked to 
  5909. // FIXME: does url_to_postid() cover all these cases already? 
  5910. // if so, then let's use it and drop the old code. 
  5911. $urltest = parse_url($pagelinkedto); 
  5912. if ( $post_ID = url_to_postid($pagelinkedto) ) { 
  5913. // $way 
  5914. } elseif ( isset( $urltest['path'] ) && preg_match('#p/[0-9]{1, }#', $urltest['path'], $match) ) { 
  5915. // the path defines the post_ID (archives/p/XXXX) 
  5916. $blah = explode('/', $match[0]); 
  5917. $post_ID = (int) $blah[1]; 
  5918. } elseif ( isset( $urltest['query'] ) && preg_match('#p=[0-9]{1, }#', $urltest['query'], $match) ) { 
  5919. // the querystring defines the post_ID (?p=XXXX) 
  5920. $blah = explode('=', $match[0]); 
  5921. $post_ID = (int) $blah[1]; 
  5922. } elseif ( isset($urltest['fragment']) ) { 
  5923. // an #anchor is there, it's either... 
  5924. if ( intval($urltest['fragment']) ) { 
  5925. // ...an integer #XXXX (simplest case) 
  5926. $post_ID = (int) $urltest['fragment']; 
  5927. } elseif ( preg_match('/post-[0-9]+/', $urltest['fragment']) ) { 
  5928. // ...a post id in the form 'post-###' 
  5929. $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); 
  5930. } elseif ( is_string($urltest['fragment']) ) { 
  5931. // ...or a string #title, a little more complicated 
  5932. $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); 
  5933. $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title ); 
  5934. if (! ($post_ID = $wpdb->get_var($sql)) ) { 
  5935. // returning unknown error '0' is better than die()ing 
  5936. return $this->pingback_error( 0, '' ); 
  5937. } else { 
  5938. // TODO: Attempt to extract a post ID from the given URL 
  5939. return $this->pingback_error( 33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); 
  5940. $post_ID = (int) $post_ID; 
  5941.  
  5942. $post = get_post($post_ID); 
  5943.  
  5944. if ( !$post ) // Post_ID not found 
  5945. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); 
  5946.  
  5947. if ( $post_ID == url_to_postid($pagelinkedfrom) ) 
  5948. return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) ); 
  5949.  
  5950. // Check if pings are on 
  5951. if ( !pings_open($post) ) 
  5952. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); 
  5953.  
  5954. // Let's check that the remote site didn't already pingback this entry 
  5955. if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) ) 
  5956. return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) ); 
  5957.  
  5958. // very stupid, but gives time to the 'from' server to publish ! 
  5959. sleep(1); 
  5960.  
  5961. $remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ); 
  5962.  
  5963. /** This filter is documented in wp-includes/class-http.php */ 
  5964. $user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) ); 
  5965.  
  5966. // Let's check the remote site 
  5967. $http_api_args = array( 
  5968. 'timeout' => 10,  
  5969. 'redirection' => 0,  
  5970. 'limit_response_size' => 153600, // 150 KB 
  5971. 'user-agent' => "$user_agent; verifying pingback from $remote_ip",  
  5972. 'headers' => array( 
  5973. 'X-Pingback-Forwarded-For' => $remote_ip,  
  5974. ),  
  5975. ); 
  5976.  
  5977. $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args ); 
  5978. $remote_source = $remote_source_original = wp_remote_retrieve_body( $request ); 
  5979.  
  5980. if ( ! $remote_source ) { 
  5981. return $this->pingback_error( 16, __( 'The source URL does not exist.' ) ); 
  5982.  
  5983. /** 
  5984. * Filters the pingback remote source. 
  5985. * 
  5986. * @since 2.5.0 
  5987. * 
  5988. * @param string $remote_source Response source for the page linked from. 
  5989. * @param string $pagelinkedto URL of the page linked to. 
  5990. */ 
  5991. $remote_source = apply_filters( 'pre_remote_source', $remote_source, $pagelinkedto ); 
  5992.  
  5993. // Work around bug in strip_tags(): 
  5994. $remote_source = str_replace( '<!DOC', '<DOC', $remote_source ); 
  5995. $remote_source = preg_replace( '/[\r\n\t ]+/', ' ', $remote_source ); // normalize spaces 
  5996. $remote_source = preg_replace( "/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $remote_source ); 
  5997.   
  5998. preg_match( '|<title>([^<]*?)</title>|is', $remote_source, $matchtitle ); 
  5999. $title = isset( $matchtitle[1] ) ? $matchtitle[1] : ''; 
  6000. if ( empty( $title ) ) { 
  6001. return $this->pingback_error( 32, __( 'We cannot find a title on that page.' ) ); 
  6002. } 
  6003.   
  6004. $remote_source = strip_tags( $remote_source, '<a>' ); // just keep the tag we need 
  6005.   
  6006. $p = explode( "\n\n", $remote_source ); 
  6007.   
  6008. $preg_target = preg_quote($pagelinkedto, '|'); 
  6009.   
  6010. foreach ( $p as $para ) { 
  6011. if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? 
  6012. preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context); 
  6013.   
  6014. // If the URL isn't in a link context, keep looking 
  6015. if ( empty($context) ) 
  6016. continue; 
  6017.   
  6018. // We're going to use this fake tag to mark the context in a bit 
  6019. // the marker is needed in case the link text appears more than once in the paragraph 
  6020. $excerpt = preg_replace('|\</?wpcontext\>|', '', $para); 
  6021.   
  6022. // prevent really long link text 
  6023. if ( strlen($context[1]) > 100 ) 
  6024. $context[1] = substr($context[1], 0, 100) . '…'; 
  6025.   
  6026. $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker 
  6027. $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker 
  6028. $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker 
  6029. $excerpt = trim($excerpt); 
  6030. $preg_marker = preg_quote($marker, '|'); 
  6031. $excerpt = preg_replace("|.*?\s(.{0, 100}$preg_marker.{0, 100})\s.*|s", '$1', $excerpt); 
  6032. $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper 
  6033. break; 
  6034. } 
  6035. } 
  6036.   
  6037. if ( empty($context) ) // Link to target not found 
  6038. 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.' ) ); 
  6039.   
  6040. $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 
  6041.   
  6042. $context = '[…] ' . esc_html( $excerpt ) . ' […]'; 
  6043. $pagelinkedfrom = $this->escape( $pagelinkedfrom ); 
  6044.   
  6045. $comment_post_ID = (int) $post_ID; 
  6046. $comment_author = $title; 
  6047. $comment_author_email = ''; 
  6048. $this->escape($comment_author); 
  6049. $comment_author_url = $pagelinkedfrom; 
  6050. $comment_content = $context; 
  6051. $this->escape($comment_content); 
  6052. $comment_type = 'pingback'; 
  6053.   
  6054. $commentdata = compact( 
  6055. 'comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email',  
  6056. 'comment_content', 'comment_type', 'remote_source', 'remote_source_original' 
  6057. ); 
  6058.   
  6059. $comment_ID = wp_new_comment($commentdata); 
  6060.   
  6061. /** 
  6062. * Fires after a post pingback has been sent. 
  6063. * 
  6064. * @since 0.71 
  6065. * 
  6066. * @param int $comment_ID Comment ID. 
  6067. */ 
  6068. do_action( 'pingback_post', $comment_ID ); 
  6069.  
  6070. /** translators: 1: URL of the page linked from, 2: URL of the page linked to */ 
  6071. return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $pagelinkedfrom, $pagelinkedto ); 
  6072.  
  6073. /** 
  6074. * Retrieve array of URLs that pingbacked the given URL. 
  6075. * 
  6076. * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html 
  6077. * 
  6078. * @since 1.5.0 
  6079. * 
  6080. * @global wpdb $wpdb WordPress database abstraction object. 
  6081. * 
  6082. * @param string $url 
  6083. * @return array|IXR_Error 
  6084. */ 
  6085. public function pingback_extensions_getPingbacks( $url ) { 
  6086. global $wpdb; 
  6087.  
  6088. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  6089. do_action( 'xmlrpc_call', 'pingback.extensions.getPingbacks' ); 
  6090.  
  6091. $url = $this->escape( $url ); 
  6092.  
  6093. $post_ID = url_to_postid($url); 
  6094. if ( !$post_ID ) { 
  6095. // We aren't sure that the resource is available and/or pingback enabled 
  6096. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); 
  6097.  
  6098. $actual_post = get_post($post_ID, ARRAY_A); 
  6099.  
  6100. if ( !$actual_post ) { 
  6101. // No such post = resource not found 
  6102. return $this->pingback_error( 32, __('The specified target URL does not exist.' ) ); 
  6103.  
  6104. $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) ); 
  6105.  
  6106. if ( !$comments ) 
  6107. return array(); 
  6108.  
  6109. $pingbacks = array(); 
  6110. foreach ( $comments as $comment ) { 
  6111. if ( 'pingback' == $comment->comment_type ) 
  6112. $pingbacks[] = $comment->comment_author_url; 
  6113.  
  6114. return $pingbacks; 
  6115.  
  6116. /** 
  6117. * Sends a pingback error based on the given error code and message. 
  6118. * 
  6119. * @since 3.6.0 
  6120. * 
  6121. * @param int $code Error code. 
  6122. * @param string $message Error message. 
  6123. * @return IXR_Error Error object. 
  6124. */ 
  6125. protected function pingback_error( $code, $message ) { 
  6126. /** 
  6127. * Filters the XML-RPC pingback error return. 
  6128. * 
  6129. * @since 3.5.1 
  6130. * 
  6131. * @param IXR_Error $error An IXR_Error object containing the error code and message. 
  6132. */ 
  6133. return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) ); 
.