wp_xmlrpc_server

WordPress XMLRPC server implementation.

Defined (1)

The class is defined in the following location(s).

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