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. */ 
  328. public function initialise_blog_option_info() { 
  329. $this->blog_options = array( 
  330. // Read only options 
  331. 'software_name' => array( 
  332. 'desc' => __( 'Software Name' ),  
  333. 'readonly' => true,  
  334. 'value' => 'WordPress' 
  335. ),  
  336. 'software_version' => array( 
  337. 'desc' => __( 'Software Version' ),  
  338. 'readonly' => true,  
  339. 'value' => get_bloginfo( 'version' ) 
  340. ),  
  341. 'blog_url' => array( 
  342. 'desc' => __( 'WordPress Address (URL)' ),  
  343. 'readonly' => true,  
  344. 'option' => 'siteurl' 
  345. ),  
  346. 'home_url' => array( 
  347. 'desc' => __( 'Site Address (URL)' ),  
  348. 'readonly' => true,  
  349. 'option' => 'home' 
  350. ),  
  351. 'login_url' => array( 
  352. 'desc' => __( 'Login Address (URL)' ),  
  353. 'readonly' => true,  
  354. 'value' => wp_login_url( ) 
  355. ),  
  356. 'admin_url' => array( 
  357. 'desc' => __( 'The URL to the admin area' ),  
  358. 'readonly' => true,  
  359. 'value' => get_admin_url( ) 
  360. ),  
  361. 'image_default_link_type' => array( 
  362. 'desc' => __( 'Image default link type' ),  
  363. 'readonly' => true,  
  364. 'option' => 'image_default_link_type' 
  365. ),  
  366. 'image_default_size' => array( 
  367. 'desc' => __( 'Image default size' ),  
  368. 'readonly' => true,  
  369. 'option' => 'image_default_size' 
  370. ),  
  371. 'image_default_align' => array( 
  372. 'desc' => __( 'Image default align' ),  
  373. 'readonly' => true,  
  374. 'option' => 'image_default_align' 
  375. ),  
  376. 'template' => array( 
  377. 'desc' => __( 'Template' ),  
  378. 'readonly' => true,  
  379. 'option' => 'template' 
  380. ),  
  381. 'stylesheet' => array( 
  382. 'desc' => __( 'Stylesheet' ),  
  383. 'readonly' => true,  
  384. 'option' => 'stylesheet' 
  385. ),  
  386. 'post_thumbnail' => array( 
  387. 'desc' => __('Post Thumbnail'),  
  388. 'readonly' => true,  
  389. 'value' => current_theme_supports( 'post-thumbnails' ) 
  390. ),  
  391.  
  392. // Updatable options 
  393. 'time_zone' => array( 
  394. 'desc' => __( 'Time Zone' ),  
  395. 'readonly' => false,  
  396. 'option' => 'gmt_offset' 
  397. ),  
  398. 'blog_title' => array( 
  399. 'desc' => __( 'Site Title' ),  
  400. 'readonly' => false,  
  401. 'option' => 'blogname' 
  402. ),  
  403. 'blog_tagline' => array( 
  404. 'desc' => __( 'Site Tagline' ),  
  405. 'readonly' => false,  
  406. 'option' => 'blogdescription' 
  407. ),  
  408. 'date_format' => array( 
  409. 'desc' => __( 'Date Format' ),  
  410. 'readonly' => false,  
  411. 'option' => 'date_format' 
  412. ),  
  413. 'time_format' => array( 
  414. 'desc' => __( 'Time Format' ),  
  415. 'readonly' => false,  
  416. 'option' => 'time_format' 
  417. ),  
  418. 'users_can_register' => array( 
  419. 'desc' => __( 'Allow new users to sign up' ),  
  420. 'readonly' => false,  
  421. 'option' => 'users_can_register' 
  422. ),  
  423. 'thumbnail_size_w' => array( 
  424. 'desc' => __( 'Thumbnail Width' ),  
  425. 'readonly' => false,  
  426. 'option' => 'thumbnail_size_w' 
  427. ),  
  428. 'thumbnail_size_h' => array( 
  429. 'desc' => __( 'Thumbnail Height' ),  
  430. 'readonly' => false,  
  431. 'option' => 'thumbnail_size_h' 
  432. ),  
  433. 'thumbnail_crop' => array( 
  434. 'desc' => __( 'Crop thumbnail to exact dimensions' ),  
  435. 'readonly' => false,  
  436. 'option' => 'thumbnail_crop' 
  437. ),  
  438. 'medium_size_w' => array( 
  439. 'desc' => __( 'Medium size image width' ),  
  440. 'readonly' => false,  
  441. 'option' => 'medium_size_w' 
  442. ),  
  443. 'medium_size_h' => array( 
  444. 'desc' => __( 'Medium size image height' ),  
  445. 'readonly' => false,  
  446. 'option' => 'medium_size_h' 
  447. ),  
  448. 'medium_large_size_w' => array( 
  449. 'desc' => __( 'Medium-Large size image width' ),  
  450. 'readonly' => false,  
  451. 'option' => 'medium_large_size_w' 
  452. ),  
  453. 'medium_large_size_h' => array( 
  454. 'desc' => __( 'Medium-Large size image height' ),  
  455. 'readonly' => false,  
  456. 'option' => 'medium_large_size_h' 
  457. ),  
  458. 'large_size_w' => array( 
  459. 'desc' => __( 'Large size image width' ),  
  460. 'readonly' => false,  
  461. 'option' => 'large_size_w' 
  462. ),  
  463. 'large_size_h' => array( 
  464. 'desc' => __( 'Large size image height' ),  
  465. 'readonly' => false,  
  466. 'option' => 'large_size_h' 
  467. ),  
  468. 'default_comment_status' => array( 
  469. 'desc' => __( 'Allow people to post comments on new articles' ),  
  470. 'readonly' => false,  
  471. 'option' => 'default_comment_status' 
  472. ),  
  473. 'default_ping_status' => array( 
  474. 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new articles' ),  
  475. 'readonly' => false,  
  476. 'option' => 'default_ping_status' 
  477. ); 
  478.  
  479. /** 
  480. * Filters the XML-RPC blog options property. 
  481. * @since 2.6.0 
  482. * @param array $blog_options An array of XML-RPC blog options. 
  483. */ 
  484. $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options ); 
  485.  
  486. /** 
  487. * Retrieve the blogs of the user. 
  488. * @since 2.6.0 
  489. * @param array $args { 
  490. * Method arguments. Note: arguments must be ordered as documented. 
  491. * @type string $username Username. 
  492. * @type string $password Password. 
  493. * } 
  494. * @return array|IXR_Error Array contains: 
  495. * - 'isAdmin' 
  496. * - 'isPrimary' - whether the blog is the user's primary blog 
  497. * - 'url' 
  498. * - 'blogid' 
  499. * - 'blogName' 
  500. * - 'xmlrpc' - url of xmlrpc endpoint 
  501. */ 
  502. public function wp_getUsersBlogs( $args ) { 
  503. if ( ! $this->minimum_args( $args, 2 ) ) { 
  504. return $this->error; 
  505.  
  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_network_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 WP_Taxonomy $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 make posts sticky.' ) ); 
  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.' ) ); 
  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, __( 'Invalid post type.' ) ); 
  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->edit_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.' ) ); 
  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. $taxonomy = (array) $taxonomy; 
  1716.  
  1717. // hold the data of the term 
  1718. $term_data = array(); 
  1719.  
  1720. $term = get_term( $term_id , $content_struct['taxonomy'] ); 
  1721.  
  1722. if ( is_wp_error( $term ) ) 
  1723. return new IXR_Error( 500, $term->get_error_message() ); 
  1724.  
  1725. if ( ! $term ) 
  1726. return new IXR_Error( 404, __( 'Invalid term ID.' ) ); 
  1727.  
  1728. if ( ! current_user_can( 'edit_term', $term_id ) ) { 
  1729. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this term.' ) ); 
  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. $term = get_term( $term_id, $taxonomy->name ); 
  1804.  
  1805. if ( is_wp_error( $term ) ) 
  1806. return new IXR_Error( 500, $term->get_error_message() ); 
  1807.  
  1808. if ( ! $term ) 
  1809. return new IXR_Error( 404, __( 'Invalid term ID.' ) ); 
  1810.  
  1811. if ( ! current_user_can( 'delete_term', $term_id ) ) { 
  1812. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this term.' ) ); 
  1813.  
  1814. $result = wp_delete_term( $term_id, $taxonomy->name ); 
  1815.  
  1816. if ( is_wp_error( $result ) ) 
  1817. return new IXR_Error( 500, $term->get_error_message() ); 
  1818.  
  1819. if ( ! $result ) 
  1820. return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) ); 
  1821.  
  1822. return $result; 
  1823.  
  1824. /** 
  1825. * Retrieve a term. 
  1826. * @since 3.4.0 
  1827. * @see get_term() 
  1828. * @param array $args { 
  1829. * Method arguments. Note: arguments must be ordered as documented. 
  1830. * @type int $blog_id Blog ID (unused). 
  1831. * @type string $username Username. 
  1832. * @type string $password Password. 
  1833. * @type string $taxnomy Taxonomy name. 
  1834. * @type string $term_id Term ID. 
  1835. * } 
  1836. * @return array|IXR_Error IXR_Error on failure, array on success, containing: 
  1837. * - 'term_id' 
  1838. * - 'name' 
  1839. * - 'slug' 
  1840. * - 'term_group' 
  1841. * - 'term_taxonomy_id' 
  1842. * - 'taxonomy' 
  1843. * - 'description' 
  1844. * - 'parent' 
  1845. * - 'count' 
  1846. */ 
  1847. public function wp_getTerm( $args ) { 
  1848. if ( ! $this->minimum_args( $args, 5 ) ) 
  1849. return $this->error; 
  1850.  
  1851. $this->escape( $args ); 
  1852.  
  1853. $username = $args[1]; 
  1854. $password = $args[2]; 
  1855. $taxonomy = $args[3]; 
  1856. $term_id = (int) $args[4]; 
  1857.  
  1858. if ( ! $user = $this->login( $username, $password ) ) 
  1859. return $this->error; 
  1860.  
  1861. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1862. do_action( 'xmlrpc_call', 'wp.getTerm' ); 
  1863.  
  1864. if ( ! taxonomy_exists( $taxonomy ) ) 
  1865. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  1866.  
  1867. $taxonomy = get_taxonomy( $taxonomy ); 
  1868.  
  1869. $term = get_term( $term_id , $taxonomy->name, ARRAY_A ); 
  1870.  
  1871. if ( is_wp_error( $term ) ) 
  1872. return new IXR_Error( 500, $term->get_error_message() ); 
  1873.  
  1874. if ( ! $term ) 
  1875. return new IXR_Error( 404, __( 'Invalid term ID.' ) ); 
  1876.  
  1877. if ( ! current_user_can( 'assign_term', $term_id ) ) { 
  1878. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign this term.' ) ); 
  1879.  
  1880. return $this->_prepare_term( $term ); 
  1881.  
  1882. /** 
  1883. * Retrieve all terms for a taxonomy. 
  1884. * @since 3.4.0 
  1885. * The optional $filter parameter modifies the query used to retrieve terms. 
  1886. * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'. 
  1887. * @see get_terms() 
  1888. * @param array $args { 
  1889. * Method arguments. Note: arguments must be ordered as documented. 
  1890. * @type int $blog_id Blog ID (unused). 
  1891. * @type string $username Username. 
  1892. * @type string $password Password. 
  1893. * @type string $taxnomy Taxonomy name. 
  1894. * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'number',  
  1895. * 'offset', 'orderby', 'order', 'hide_empty', and 'search'. Default empty array. 
  1896. * } 
  1897. * @return array|IXR_Error An associative array of terms data on success, IXR_Error instance otherwise. 
  1898. */ 
  1899. public function wp_getTerms( $args ) { 
  1900. if ( ! $this->minimum_args( $args, 4 ) ) 
  1901. return $this->error; 
  1902.  
  1903. $this->escape( $args ); 
  1904.  
  1905. $username = $args[1]; 
  1906. $password = $args[2]; 
  1907. $taxonomy = $args[3]; 
  1908. $filter = isset( $args[4] ) ? $args[4] : array(); 
  1909.  
  1910. if ( ! $user = $this->login( $username, $password ) ) 
  1911. return $this->error; 
  1912.  
  1913. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1914. do_action( 'xmlrpc_call', 'wp.getTerms' ); 
  1915.  
  1916. if ( ! taxonomy_exists( $taxonomy ) ) 
  1917. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  1918.  
  1919. $taxonomy = get_taxonomy( $taxonomy ); 
  1920.  
  1921. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) 
  1922. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); 
  1923.  
  1924. $query = array(); 
  1925.  
  1926. if ( isset( $filter['number'] ) ) 
  1927. $query['number'] = absint( $filter['number'] ); 
  1928.  
  1929. if ( isset( $filter['offset'] ) ) 
  1930. $query['offset'] = absint( $filter['offset'] ); 
  1931.  
  1932. if ( isset( $filter['orderby'] ) ) { 
  1933. $query['orderby'] = $filter['orderby']; 
  1934.  
  1935. if ( isset( $filter['order'] ) ) 
  1936. $query['order'] = $filter['order']; 
  1937.  
  1938. if ( isset( $filter['hide_empty'] ) ) 
  1939. $query['hide_empty'] = $filter['hide_empty']; 
  1940. else 
  1941. $query['get'] = 'all'; 
  1942.  
  1943. if ( isset( $filter['search'] ) ) 
  1944. $query['search'] = $filter['search']; 
  1945.  
  1946. $terms = get_terms( $taxonomy->name, $query ); 
  1947.  
  1948. if ( is_wp_error( $terms ) ) 
  1949. return new IXR_Error( 500, $terms->get_error_message() ); 
  1950.  
  1951. $struct = array(); 
  1952.  
  1953. foreach ( $terms as $term ) { 
  1954. $struct[] = $this->_prepare_term( $term ); 
  1955.  
  1956. return $struct; 
  1957.  
  1958. /** 
  1959. * Retrieve a taxonomy. 
  1960. * @since 3.4.0 
  1961. * @see get_taxonomy() 
  1962. * @param array $args { 
  1963. * Method arguments. Note: arguments must be ordered as documented. 
  1964. * @type int $blog_id Blog ID (unused). 
  1965. * @type string $username Username. 
  1966. * @type string $password Password. 
  1967. * @type string $taxnomy Taxonomy name. 
  1968. * @type array $fields Optional. Array of taxonomy fields to limit to in the return. 
  1969. * Accepts 'labels', 'cap', 'menu', and 'object_type'. 
  1970. * Default empty array. 
  1971. * } 
  1972. * @return array|IXR_Error An array of taxonomy data on success, IXR_Error instance otherwise. 
  1973. */ 
  1974. public function wp_getTaxonomy( $args ) { 
  1975. if ( ! $this->minimum_args( $args, 4 ) ) 
  1976. return $this->error; 
  1977.  
  1978. $this->escape( $args ); 
  1979.  
  1980. $username = $args[1]; 
  1981. $password = $args[2]; 
  1982. $taxonomy = $args[3]; 
  1983.  
  1984. if ( isset( $args[4] ) ) { 
  1985. $fields = $args[4]; 
  1986. } else { 
  1987. /** 
  1988. * Filters the taxonomy query fields used by the given XML-RPC method. 
  1989. * @since 3.4.0 
  1990. * @param array $fields An array of taxonomy fields to retrieve. 
  1991. * @param string $method The method name. 
  1992. */ 
  1993. $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' ); 
  1994.  
  1995. if ( ! $user = $this->login( $username, $password ) ) 
  1996. return $this->error; 
  1997.  
  1998. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  1999. do_action( 'xmlrpc_call', 'wp.getTaxonomy' ); 
  2000.  
  2001. if ( ! taxonomy_exists( $taxonomy ) ) 
  2002. return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); 
  2003.  
  2004. $taxonomy = get_taxonomy( $taxonomy ); 
  2005.  
  2006. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) 
  2007. return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); 
  2008.  
  2009. return $this->_prepare_taxonomy( $taxonomy, $fields ); 
  2010.  
  2011. /** 
  2012. * Retrieve all taxonomies. 
  2013. * @since 3.4.0 
  2014. * @see get_taxonomies() 
  2015. * @param array $args { 
  2016. * Method arguments. Note: arguments must be ordered as documented. 
  2017. * @type int $blog_id Blog ID (unused). 
  2018. * @type string $username Username. 
  2019. * @type string $password Password. 
  2020. * @type array $filter Optional. An array of arguments for retrieving taxonomies. 
  2021. * @type array $fields Optional. The subset of taxonomy fields to return. 
  2022. * } 
  2023. * @return array|IXR_Error An associative array of taxonomy data with returned fields determined 
  2024. * by `$fields`, or an IXR_Error instance on failure. 
  2025. */ 
  2026. public function wp_getTaxonomies( $args ) { 
  2027. if ( ! $this->minimum_args( $args, 3 ) ) 
  2028. return $this->error; 
  2029.  
  2030. $this->escape( $args ); 
  2031.  
  2032. $username = $args[1]; 
  2033. $password = $args[2]; 
  2034. $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); 
  2035.  
  2036. if ( isset( $args[4] ) ) { 
  2037. $fields = $args[4]; 
  2038. } else { 
  2039. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2040. $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' ); 
  2041.  
  2042. if ( ! $user = $this->login( $username, $password ) ) 
  2043. return $this->error; 
  2044.  
  2045. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2046. do_action( 'xmlrpc_call', 'wp.getTaxonomies' ); 
  2047.  
  2048. $taxonomies = get_taxonomies( $filter, 'objects' ); 
  2049.  
  2050. // holds all the taxonomy data 
  2051. $struct = array(); 
  2052.  
  2053. foreach ( $taxonomies as $taxonomy ) { 
  2054. // capability check for post_types 
  2055. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) 
  2056. continue; 
  2057.  
  2058. $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields ); 
  2059.  
  2060. return $struct; 
  2061.  
  2062. /** 
  2063. * Retrieve a user. 
  2064. * The optional $fields parameter specifies what fields will be included 
  2065. * in the response array. This should be a list of field names. 'user_id' will 
  2066. * always be included in the response regardless of the value of $fields. 
  2067. * Instead of, or in addition to, individual field names, conceptual group 
  2068. * names can be used to specify multiple fields. The available conceptual 
  2069. * groups are 'basic' and 'all'. 
  2070. * @uses get_userdata() 
  2071. * @param array $args { 
  2072. * Method arguments. Note: arguments must be ordered as documented. 
  2073. * @type int $blog_id (unused) 
  2074. * @type string $username 
  2075. * @type string $password 
  2076. * @type int $user_id 
  2077. * @type array $fields (optional) 
  2078. * } 
  2079. * @return array|IXR_Error Array contains (based on $fields parameter): 
  2080. * - 'user_id' 
  2081. * - 'username' 
  2082. * - 'first_name' 
  2083. * - 'last_name' 
  2084. * - 'registered' 
  2085. * - 'bio' 
  2086. * - 'email' 
  2087. * - 'nickname' 
  2088. * - 'nicename' 
  2089. * - 'url' 
  2090. * - 'display_name' 
  2091. * - 'roles' 
  2092. */ 
  2093. public function wp_getUser( $args ) { 
  2094. if ( ! $this->minimum_args( $args, 4 ) ) 
  2095. return $this->error; 
  2096.  
  2097. $this->escape( $args ); 
  2098.  
  2099. $username = $args[1]; 
  2100. $password = $args[2]; 
  2101. $user_id = (int) $args[3]; 
  2102.  
  2103. if ( isset( $args[4] ) ) { 
  2104. $fields = $args[4]; 
  2105. } else { 
  2106. /** 
  2107. * Filters the default user query fields used by the given XML-RPC method. 
  2108. * @since 3.5.0 
  2109. * @param array $fields User query fields for given method. Default 'all'. 
  2110. * @param string $method The method name. 
  2111. */ 
  2112. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' ); 
  2113.  
  2114. if ( ! $user = $this->login( $username, $password ) ) 
  2115. return $this->error; 
  2116.  
  2117. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2118. do_action( 'xmlrpc_call', 'wp.getUser' ); 
  2119.  
  2120. if ( ! current_user_can( 'edit_user', $user_id ) ) 
  2121. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this user.' ) ); 
  2122.  
  2123. $user_data = get_userdata( $user_id ); 
  2124.  
  2125. if ( ! $user_data ) 
  2126. return new IXR_Error( 404, __( 'Invalid user ID.' ) ); 
  2127.  
  2128. return $this->_prepare_user( $user_data, $fields ); 
  2129.  
  2130. /** 
  2131. * Retrieve users. 
  2132. * The optional $filter parameter modifies the query used to retrieve users. 
  2133. * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role',  
  2134. * 'who', 'orderby', and 'order'. 
  2135. * The optional $fields parameter specifies what fields will be included 
  2136. * in the response array. 
  2137. * @uses get_users() 
  2138. * @see wp_getUser() for more on $fields and return values 
  2139. * @param array $args { 
  2140. * Method arguments. Note: arguments must be ordered as documented. 
  2141. * @type int $blog_id (unused) 
  2142. * @type string $username 
  2143. * @type string $password 
  2144. * @type array $filter (optional) 
  2145. * @type array $fields (optional) 
  2146. * } 
  2147. * @return array|IXR_Error users data 
  2148. */ 
  2149. public function wp_getUsers( $args ) { 
  2150. if ( ! $this->minimum_args( $args, 3 ) ) 
  2151. return $this->error; 
  2152.  
  2153. $this->escape( $args ); 
  2154.  
  2155. $username = $args[1]; 
  2156. $password = $args[2]; 
  2157. $filter = isset( $args[3] ) ? $args[3] : array(); 
  2158.  
  2159. if ( isset( $args[4] ) ) { 
  2160. $fields = $args[4]; 
  2161. } else { 
  2162. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2163. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' ); 
  2164.  
  2165. if ( ! $user = $this->login( $username, $password ) ) 
  2166. return $this->error; 
  2167.  
  2168. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2169. do_action( 'xmlrpc_call', 'wp.getUsers' ); 
  2170.  
  2171. if ( ! current_user_can( 'list_users' ) ) 
  2172. return new IXR_Error( 401, __( 'Sorry, you are not allowed to list users.' ) ); 
  2173.  
  2174. $query = array( 'fields' => 'all_with_meta' ); 
  2175.  
  2176. $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50; 
  2177. $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0; 
  2178.  
  2179. if ( isset( $filter['orderby'] ) ) { 
  2180. $query['orderby'] = $filter['orderby']; 
  2181.  
  2182. if ( isset( $filter['order'] ) ) 
  2183. $query['order'] = $filter['order']; 
  2184.  
  2185. if ( isset( $filter['role'] ) ) { 
  2186. if ( get_role( $filter['role'] ) === null ) 
  2187. return new IXR_Error( 403, __( 'Invalid role.' ) ); 
  2188.  
  2189. $query['role'] = $filter['role']; 
  2190.  
  2191. if ( isset( $filter['who'] ) ) { 
  2192. $query['who'] = $filter['who']; 
  2193.  
  2194. $users = get_users( $query ); 
  2195.  
  2196. $_users = array(); 
  2197. foreach ( $users as $user_data ) { 
  2198. if ( current_user_can( 'edit_user', $user_data->ID ) ) 
  2199. $_users[] = $this->_prepare_user( $user_data, $fields ); 
  2200. return $_users; 
  2201.  
  2202. /** 
  2203. * Retrieve information about the requesting user. 
  2204. * @uses get_userdata() 
  2205. * @param array $args { 
  2206. * Method arguments. Note: arguments must be ordered as documented. 
  2207. * @type int $blog_id (unused) 
  2208. * @type string $username 
  2209. * @type string $password 
  2210. * @type array $fields (optional) 
  2211. * } 
  2212. * @return array|IXR_Error (@see wp_getUser) 
  2213. */ 
  2214. public function wp_getProfile( $args ) { 
  2215. if ( ! $this->minimum_args( $args, 3 ) ) 
  2216. return $this->error; 
  2217.  
  2218. $this->escape( $args ); 
  2219.  
  2220. $username = $args[1]; 
  2221. $password = $args[2]; 
  2222.  
  2223. if ( isset( $args[3] ) ) { 
  2224. $fields = $args[3]; 
  2225. } else { 
  2226. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2227. $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' ); 
  2228.  
  2229. if ( ! $user = $this->login( $username, $password ) ) 
  2230. return $this->error; 
  2231.  
  2232. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2233. do_action( 'xmlrpc_call', 'wp.getProfile' ); 
  2234.  
  2235. if ( ! current_user_can( 'edit_user', $user->ID ) ) 
  2236. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); 
  2237.  
  2238. $user_data = get_userdata( $user->ID ); 
  2239.  
  2240. return $this->_prepare_user( $user_data, $fields ); 
  2241.  
  2242. /** 
  2243. * Edit user's profile. 
  2244. * @uses wp_update_user() 
  2245. * @param array $args { 
  2246. * Method arguments. Note: arguments must be ordered as documented. 
  2247. * @type int $blog_id (unused) 
  2248. * @type string $username 
  2249. * @type string $password 
  2250. * @type array $content_struct It can optionally contain: 
  2251. * - 'first_name' 
  2252. * - 'last_name' 
  2253. * - 'website' 
  2254. * - 'display_name' 
  2255. * - 'nickname' 
  2256. * - 'nicename' 
  2257. * - 'bio' 
  2258. * } 
  2259. * @return true|IXR_Error True, on success. 
  2260. */ 
  2261. public function wp_editProfile( $args ) { 
  2262. if ( ! $this->minimum_args( $args, 4 ) ) 
  2263. return $this->error; 
  2264.  
  2265. $this->escape( $args ); 
  2266.  
  2267. $username = $args[1]; 
  2268. $password = $args[2]; 
  2269. $content_struct = $args[3]; 
  2270.  
  2271. if ( ! $user = $this->login( $username, $password ) ) 
  2272. return $this->error; 
  2273.  
  2274. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2275. do_action( 'xmlrpc_call', 'wp.editProfile' ); 
  2276.  
  2277. if ( ! current_user_can( 'edit_user', $user->ID ) ) 
  2278. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); 
  2279.  
  2280. // holds data of the user 
  2281. $user_data = array(); 
  2282. $user_data['ID'] = $user->ID; 
  2283.  
  2284. // only set the user details if it was given 
  2285. if ( isset( $content_struct['first_name'] ) ) 
  2286. $user_data['first_name'] = $content_struct['first_name']; 
  2287.  
  2288. if ( isset( $content_struct['last_name'] ) ) 
  2289. $user_data['last_name'] = $content_struct['last_name']; 
  2290.  
  2291. if ( isset( $content_struct['url'] ) ) 
  2292. $user_data['user_url'] = $content_struct['url']; 
  2293.  
  2294. if ( isset( $content_struct['display_name'] ) ) 
  2295. $user_data['display_name'] = $content_struct['display_name']; 
  2296.  
  2297. if ( isset( $content_struct['nickname'] ) ) 
  2298. $user_data['nickname'] = $content_struct['nickname']; 
  2299.  
  2300. if ( isset( $content_struct['nicename'] ) ) 
  2301. $user_data['user_nicename'] = $content_struct['nicename']; 
  2302.  
  2303. if ( isset( $content_struct['bio'] ) ) 
  2304. $user_data['description'] = $content_struct['bio']; 
  2305.  
  2306. $result = wp_update_user( $user_data ); 
  2307.  
  2308. if ( is_wp_error( $result ) ) 
  2309. return new IXR_Error( 500, $result->get_error_message() ); 
  2310.  
  2311. if ( ! $result ) 
  2312. return new IXR_Error( 500, __( 'Sorry, the user cannot be updated.' ) ); 
  2313.  
  2314. return true; 
  2315.  
  2316. /** 
  2317. * Retrieve page. 
  2318. * @since 2.2.0 
  2319. * @param array $args { 
  2320. * Method arguments. Note: arguments must be ordered as documented. 
  2321. * @type int $blog_id (unused) 
  2322. * @type int $page_id 
  2323. * @type string $username 
  2324. * @type string $password 
  2325. * } 
  2326. * @return array|IXR_Error 
  2327. */ 
  2328. public function wp_getPage( $args ) { 
  2329. $this->escape( $args ); 
  2330.  
  2331. $page_id = (int) $args[1]; 
  2332. $username = $args[2]; 
  2333. $password = $args[3]; 
  2334.  
  2335. if ( !$user = $this->login($username, $password) ) { 
  2336. return $this->error; 
  2337.  
  2338. $page = get_post($page_id); 
  2339. if ( ! $page ) 
  2340. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  2341.  
  2342. if ( !current_user_can( 'edit_page', $page_id ) ) 
  2343. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); 
  2344.  
  2345. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2346. do_action( 'xmlrpc_call', 'wp.getPage' ); 
  2347.  
  2348. // If we found the page then format the data. 
  2349. if ( $page->ID && ($page->post_type == 'page') ) { 
  2350. return $this->_prepare_page( $page ); 
  2351. // If the page doesn't exist indicate that. 
  2352. else { 
  2353. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); 
  2354.  
  2355. /** 
  2356. * Retrieve Pages. 
  2357. * @since 2.2.0 
  2358. * @param array $args { 
  2359. * Method arguments. Note: arguments must be ordered as documented. 
  2360. * @type int $blog_id (unused) 
  2361. * @type string $username 
  2362. * @type string $password 
  2363. * @type int $num_pages 
  2364. * } 
  2365. * @return array|IXR_Error 
  2366. */ 
  2367. public function wp_getPages( $args ) { 
  2368. $this->escape( $args ); 
  2369.  
  2370. $username = $args[1]; 
  2371. $password = $args[2]; 
  2372. $num_pages = isset($args[3]) ? (int) $args[3] : 10; 
  2373.  
  2374. if ( !$user = $this->login($username, $password) ) 
  2375. return $this->error; 
  2376.  
  2377. if ( !current_user_can( 'edit_pages' ) ) 
  2378. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); 
  2379.  
  2380. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2381. do_action( 'xmlrpc_call', 'wp.getPages' ); 
  2382.  
  2383. $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) ); 
  2384. $num_pages = count($pages); 
  2385.  
  2386. // If we have pages, put together their info. 
  2387. if ( $num_pages >= 1 ) { 
  2388. $pages_struct = array(); 
  2389.  
  2390. foreach ($pages as $page) { 
  2391. if ( current_user_can( 'edit_page', $page->ID ) ) 
  2392. $pages_struct[] = $this->_prepare_page( $page ); 
  2393.  
  2394. return $pages_struct; 
  2395.  
  2396. return array(); 
  2397.  
  2398. /** 
  2399. * Create new page. 
  2400. * @since 2.2.0 
  2401. * @see wp_xmlrpc_server::mw_newPost() 
  2402. * @param array $args { 
  2403. * Method arguments. Note: arguments must be ordered as documented. 
  2404. * @type int $blog_id (unused) 
  2405. * @type string $username 
  2406. * @type string $password 
  2407. * @type array $content_struct 
  2408. * } 
  2409. * @return int|IXR_Error 
  2410. */ 
  2411. public function wp_newPage( $args ) { 
  2412. // Items not escaped here will be escaped in newPost. 
  2413. $username = $this->escape( $args[1] ); 
  2414. $password = $this->escape( $args[2] ); 
  2415.  
  2416. if ( !$user = $this->login($username, $password) ) 
  2417. return $this->error; 
  2418.  
  2419. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2420. do_action( 'xmlrpc_call', 'wp.newPage' ); 
  2421.  
  2422. // Mark this as content for a page. 
  2423. $args[3]["post_type"] = 'page'; 
  2424.  
  2425. // Let mw_newPost do all of the heavy lifting. 
  2426. return $this->mw_newPost( $args ); 
  2427.  
  2428. /** 
  2429. * Delete page. 
  2430. * @since 2.2.0 
  2431. * @param array $args { 
  2432. * Method arguments. Note: arguments must be ordered as documented. 
  2433. * @type int $blog_id (unused) 
  2434. * @type string $username 
  2435. * @type string $password 
  2436. * @type int $page_id 
  2437. * } 
  2438. * @return true|IXR_Error True, if success. 
  2439. */ 
  2440. public function wp_deletePage( $args ) { 
  2441. $this->escape( $args ); 
  2442.  
  2443. $username = $args[1]; 
  2444. $password = $args[2]; 
  2445. $page_id = (int) $args[3]; 
  2446.  
  2447. if ( !$user = $this->login($username, $password) ) 
  2448. return $this->error; 
  2449.  
  2450. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2451. do_action( 'xmlrpc_call', 'wp.deletePage' ); 
  2452.  
  2453. // Get the current page based on the page_id and 
  2454. // make sure it is a page and not a post. 
  2455. $actual_page = get_post($page_id, ARRAY_A); 
  2456. if ( !$actual_page || ($actual_page['post_type'] != 'page') ) 
  2457. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); 
  2458.  
  2459. // Make sure the user can delete pages. 
  2460. if ( !current_user_can('delete_page', $page_id) ) 
  2461. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this page.' ) ); 
  2462.  
  2463. // Attempt to delete the page. 
  2464. $result = wp_delete_post($page_id); 
  2465. if ( !$result ) 
  2466. return new IXR_Error( 500, __( 'Failed to delete the page.' ) ); 
  2467.  
  2468. /** 
  2469. * Fires after a page has been successfully deleted via XML-RPC. 
  2470. * @since 3.4.0 
  2471. * @param int $page_id ID of the deleted page. 
  2472. * @param array $args An array of arguments to delete the page. 
  2473. */ 
  2474. do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args ); 
  2475.  
  2476. return true; 
  2477.  
  2478. /** 
  2479. * Edit page. 
  2480. * @since 2.2.0 
  2481. * @param array $args { 
  2482. * Method arguments. Note: arguments must be ordered as documented. 
  2483. * @type int $blog_id (unused) 
  2484. * @type int $page_id 
  2485. * @type string $username 
  2486. * @type string $password 
  2487. * @type string $content 
  2488. * @type string $publish 
  2489. * } 
  2490. * @return array|IXR_Error 
  2491. */ 
  2492. public function wp_editPage( $args ) { 
  2493. // Items will be escaped in mw_editPost. 
  2494. $page_id = (int) $args[1]; 
  2495. $username = $args[2]; 
  2496. $password = $args[3]; 
  2497. $content = $args[4]; 
  2498. $publish = $args[5]; 
  2499.  
  2500. $escaped_username = $this->escape( $username ); 
  2501. $escaped_password = $this->escape( $password ); 
  2502.  
  2503. if ( !$user = $this->login( $escaped_username, $escaped_password ) ) { 
  2504. return $this->error; 
  2505.  
  2506. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2507. do_action( 'xmlrpc_call', 'wp.editPage' ); 
  2508.  
  2509. // Get the page data and make sure it is a page. 
  2510. $actual_page = get_post($page_id, ARRAY_A); 
  2511. if ( !$actual_page || ($actual_page['post_type'] != 'page') ) 
  2512. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); 
  2513.  
  2514. // Make sure the user is allowed to edit pages. 
  2515. if ( !current_user_can('edit_page', $page_id) ) 
  2516. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); 
  2517.  
  2518. // Mark this as content for a page. 
  2519. $content['post_type'] = 'page'; 
  2520.  
  2521. // Arrange args in the way mw_editPost understands. 
  2522. $args = array( 
  2523. $page_id,  
  2524. $username,  
  2525. $password,  
  2526. $content,  
  2527. $publish 
  2528. ); 
  2529.  
  2530. // Let mw_editPost do all of the heavy lifting. 
  2531. return $this->mw_editPost( $args ); 
  2532.  
  2533. /** 
  2534. * Retrieve page list. 
  2535. * @since 2.2.0 
  2536. * @global wpdb $wpdb WordPress database abstraction object. 
  2537. * @param array $args { 
  2538. * Method arguments. Note: arguments must be ordered as documented. 
  2539. * @type int $blog_id (unused) 
  2540. * @type string $username 
  2541. * @type string $password 
  2542. * } 
  2543. * @return array|IXR_Error 
  2544. */ 
  2545. public function wp_getPageList( $args ) { 
  2546. global $wpdb; 
  2547.  
  2548. $this->escape( $args ); 
  2549.  
  2550. $username = $args[1]; 
  2551. $password = $args[2]; 
  2552.  
  2553. if ( !$user = $this->login($username, $password) ) 
  2554. return $this->error; 
  2555.  
  2556. if ( !current_user_can( 'edit_pages' ) ) 
  2557. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); 
  2558.  
  2559. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2560. do_action( 'xmlrpc_call', 'wp.getPageList' ); 
  2561.  
  2562. // Get list of pages ids and titles 
  2563. $page_list = $wpdb->get_results(" 
  2564. SELECT ID page_id,  
  2565. post_title page_title,  
  2566. post_parent page_parent_id,  
  2567. post_date_gmt,  
  2568. post_date,  
  2569. post_status 
  2570. FROM {$wpdb->posts} 
  2571. WHERE post_type = 'page' 
  2572. ORDER BY ID 
  2573. "); 
  2574.  
  2575. // The date needs to be formatted properly. 
  2576. $num_pages = count($page_list); 
  2577. for ( $i = 0; $i < $num_pages; $i++ ) { 
  2578. $page_list[$i]->dateCreated = $this->_convert_date( $page_list[$i]->post_date ); 
  2579. $page_list[$i]->date_created_gmt = $this->_convert_date_gmt( $page_list[$i]->post_date_gmt, $page_list[$i]->post_date ); 
  2580.  
  2581. unset($page_list[$i]->post_date_gmt); 
  2582. unset($page_list[$i]->post_date); 
  2583. unset($page_list[$i]->post_status); 
  2584.  
  2585. return $page_list; 
  2586.  
  2587. /** 
  2588. * Retrieve authors list. 
  2589. * @since 2.2.0 
  2590. * @param array $args { 
  2591. * Method arguments. Note: arguments must be ordered as documented. 
  2592. * @type int $blog_id (unused) 
  2593. * @type string $username 
  2594. * @type string $password 
  2595. * } 
  2596. * @return array|IXR_Error 
  2597. */ 
  2598. public function wp_getAuthors( $args ) { 
  2599. $this->escape( $args ); 
  2600.  
  2601. $username = $args[1]; 
  2602. $password = $args[2]; 
  2603.  
  2604. if ( !$user = $this->login($username, $password) ) 
  2605. return $this->error; 
  2606.  
  2607. if ( !current_user_can('edit_posts') ) 
  2608. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  2609.  
  2610. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2611. do_action( 'xmlrpc_call', 'wp.getAuthors' ); 
  2612.  
  2613. $authors = array(); 
  2614. foreach ( get_users( array( 'fields' => array('ID', 'user_login', 'display_name') ) ) as $user ) { 
  2615. $authors[] = array( 
  2616. 'user_id' => $user->ID,  
  2617. 'user_login' => $user->user_login,  
  2618. 'display_name' => $user->display_name 
  2619. ); 
  2620.  
  2621. return $authors; 
  2622.  
  2623. /** 
  2624. * Get list of all tags 
  2625. * @since 2.7.0 
  2626. * @param array $args { 
  2627. * Method arguments. Note: arguments must be ordered as documented. 
  2628. * @type int $blog_id (unused) 
  2629. * @type string $username 
  2630. * @type string $password 
  2631. * } 
  2632. * @return array|IXR_Error 
  2633. */ 
  2634. public function wp_getTags( $args ) { 
  2635. $this->escape( $args ); 
  2636.  
  2637. $username = $args[1]; 
  2638. $password = $args[2]; 
  2639.  
  2640. if ( !$user = $this->login($username, $password) ) 
  2641. return $this->error; 
  2642.  
  2643. if ( !current_user_can( 'edit_posts' ) ) 
  2644. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) ); 
  2645.  
  2646. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2647. do_action( 'xmlrpc_call', 'wp.getKeywords' ); 
  2648.  
  2649. $tags = array(); 
  2650.  
  2651. if ( $all_tags = get_tags() ) { 
  2652. foreach ( (array) $all_tags as $tag ) { 
  2653. $struct = array(); 
  2654. $struct['tag_id'] = $tag->term_id; 
  2655. $struct['name'] = $tag->name; 
  2656. $struct['count'] = $tag->count; 
  2657. $struct['slug'] = $tag->slug; 
  2658. $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) ); 
  2659. $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) ); 
  2660.  
  2661. $tags[] = $struct; 
  2662.  
  2663. return $tags; 
  2664.  
  2665. /** 
  2666. * Create new category. 
  2667. * @since 2.2.0 
  2668. * @param array $args { 
  2669. * Method arguments. Note: arguments must be ordered as documented. 
  2670. * @type int $blog_id (unused) 
  2671. * @type string $username 
  2672. * @type string $password 
  2673. * @type array $category 
  2674. * } 
  2675. * @return int|IXR_Error Category ID. 
  2676. */ 
  2677. public function wp_newCategory( $args ) { 
  2678. $this->escape( $args ); 
  2679.  
  2680. $username = $args[1]; 
  2681. $password = $args[2]; 
  2682. $category = $args[3]; 
  2683.  
  2684. if ( !$user = $this->login($username, $password) ) 
  2685. return $this->error; 
  2686.  
  2687. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2688. do_action( 'xmlrpc_call', 'wp.newCategory' ); 
  2689.  
  2690. // Make sure the user is allowed to add a category. 
  2691. if ( !current_user_can('manage_categories') ) 
  2692. return new IXR_Error(401, __('Sorry, you are not allowed to add a category.')); 
  2693.  
  2694. // If no slug was provided make it empty so that 
  2695. // WordPress will generate one. 
  2696. if ( empty($category['slug']) ) 
  2697. $category['slug'] = ''; 
  2698.  
  2699. // If no parent_id was provided make it empty 
  2700. // so that it will be a top level page (no parent). 
  2701. if ( !isset($category['parent_id']) ) 
  2702. $category['parent_id'] = ''; 
  2703.  
  2704. // If no description was provided make it empty. 
  2705. if ( empty($category["description"]) ) 
  2706. $category["description"] = ""; 
  2707.  
  2708. $new_category = array( 
  2709. 'cat_name' => $category['name'],  
  2710. 'category_nicename' => $category['slug'],  
  2711. 'category_parent' => $category['parent_id'],  
  2712. 'category_description' => $category['description'] 
  2713. ); 
  2714.  
  2715. $cat_id = wp_insert_category($new_category, true); 
  2716. if ( is_wp_error( $cat_id ) ) { 
  2717. if ( 'term_exists' == $cat_id->get_error_code() ) 
  2718. return (int) $cat_id->get_error_data(); 
  2719. else 
  2720. return new IXR_Error(500, __('Sorry, the new category failed.')); 
  2721. } elseif ( ! $cat_id ) { 
  2722. return new IXR_Error(500, __('Sorry, the new category failed.')); 
  2723.  
  2724. /** 
  2725. * Fires after a new category has been successfully created via XML-RPC. 
  2726. * @since 3.4.0 
  2727. * @param int $cat_id ID of the new category. 
  2728. * @param array $args An array of new category arguments. 
  2729. */ 
  2730. do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args ); 
  2731.  
  2732. return $cat_id; 
  2733.  
  2734. /** 
  2735. * Remove category. 
  2736. * @since 2.5.0 
  2737. * @param array $args { 
  2738. * Method arguments. Note: arguments must be ordered as documented. 
  2739. * @type int $blog_id (unused) 
  2740. * @type string $username 
  2741. * @type string $password 
  2742. * @type int $category_id 
  2743. * } 
  2744. * @return bool|IXR_Error See wp_delete_term() for return info. 
  2745. */ 
  2746. public function wp_deleteCategory( $args ) { 
  2747. $this->escape( $args ); 
  2748.  
  2749. $username = $args[1]; 
  2750. $password = $args[2]; 
  2751. $category_id = (int) $args[3]; 
  2752.  
  2753. if ( !$user = $this->login($username, $password) ) 
  2754. return $this->error; 
  2755.  
  2756. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2757. do_action( 'xmlrpc_call', 'wp.deleteCategory' ); 
  2758.  
  2759. if ( !current_user_can('manage_categories') ) 
  2760. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete a category.' ) ); 
  2761.  
  2762. $status = wp_delete_term( $category_id, 'category' ); 
  2763.  
  2764. if ( true == $status ) { 
  2765. /** 
  2766. * Fires after a category has been successfully deleted via XML-RPC. 
  2767. * @since 3.4.0 
  2768. * @param int $category_id ID of the deleted category. 
  2769. * @param array $args An array of arguments to delete the category. 
  2770. */ 
  2771. do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); 
  2772.  
  2773. return $status; 
  2774.  
  2775. /** 
  2776. * Retrieve category list. 
  2777. * @since 2.2.0 
  2778. * @param array $args { 
  2779. * Method arguments. Note: arguments must be ordered as documented. 
  2780. * @type int $blog_id (unused) 
  2781. * @type string $username 
  2782. * @type string $password 
  2783. * @type array $category 
  2784. * @type int $max_results 
  2785. * } 
  2786. * @return array|IXR_Error 
  2787. */ 
  2788. public function wp_suggestCategories( $args ) { 
  2789. $this->escape( $args ); 
  2790.  
  2791. $username = $args[1]; 
  2792. $password = $args[2]; 
  2793. $category = $args[3]; 
  2794. $max_results = (int) $args[4]; 
  2795.  
  2796. if ( !$user = $this->login($username, $password) ) 
  2797. return $this->error; 
  2798.  
  2799. if ( !current_user_can( 'edit_posts' ) ) 
  2800. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 
  2801.  
  2802. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2803. do_action( 'xmlrpc_call', 'wp.suggestCategories' ); 
  2804.  
  2805. $category_suggestions = array(); 
  2806. $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 
  2807. foreach ( (array) get_categories($args) as $cat ) { 
  2808. $category_suggestions[] = array( 
  2809. 'category_id' => $cat->term_id,  
  2810. 'category_name' => $cat->name 
  2811. ); 
  2812.  
  2813. return $category_suggestions; 
  2814.  
  2815. /** 
  2816. * Retrieve comment. 
  2817. * @since 2.7.0 
  2818. * @param array $args { 
  2819. * Method arguments. Note: arguments must be ordered as documented. 
  2820. * @type int $blog_id (unused) 
  2821. * @type string $username 
  2822. * @type string $password 
  2823. * @type int $comment_id 
  2824. * } 
  2825. * @return array|IXR_Error 
  2826. */ 
  2827. public function wp_getComment($args) { 
  2828. $this->escape($args); 
  2829.  
  2830. $username = $args[1]; 
  2831. $password = $args[2]; 
  2832. $comment_id = (int) $args[3]; 
  2833.  
  2834. if ( ! $user = $this->login( $username, $password ) ) { 
  2835. return $this->error; 
  2836.  
  2837. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2838. do_action( 'xmlrpc_call', 'wp.getComment' ); 
  2839.  
  2840. if ( ! $comment = get_comment( $comment_id ) ) { 
  2841. return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 
  2842.  
  2843. if ( ! current_user_can( 'edit_comment', $comment_id ) ) { 
  2844. return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); 
  2845.  
  2846. return $this->_prepare_comment( $comment ); 
  2847.  
  2848. /** 
  2849. * Retrieve comments. 
  2850. * Besides the common blog_id (unused), username, and password arguments, it takes a filter 
  2851. * array as last argument. 
  2852. * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'. 
  2853. * The defaults are as follows: 
  2854. * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold') 
  2855. * - 'post_id' - Default is ''. The post where the comment is posted. Empty string shows all comments. 
  2856. * - 'number' - Default is 10. Total number of media items to retrieve. 
  2857. * - 'offset' - Default is 0. See WP_Query::query() for more. 
  2858. * @since 2.7.0 
  2859. * @param array $args { 
  2860. * Method arguments. Note: arguments must be ordered as documented. 
  2861. * @type int $blog_id (unused) 
  2862. * @type string $username 
  2863. * @type string $password 
  2864. * @type array $struct 
  2865. * } 
  2866. * @return array|IXR_Error Contains a collection of comments. See wp_xmlrpc_server::wp_getComment() for a description of each item contents 
  2867. */ 
  2868. public function wp_getComments( $args ) { 
  2869. $this->escape( $args ); 
  2870.  
  2871. $username = $args[1]; 
  2872. $password = $args[2]; 
  2873. $struct = isset( $args[3] ) ? $args[3] : array(); 
  2874.  
  2875. if ( ! $user = $this->login( $username, $password ) ) { 
  2876. return $this->error; 
  2877.  
  2878. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2879. do_action( 'xmlrpc_call', 'wp.getComments' ); 
  2880.  
  2881. if ( isset( $struct['status'] ) ) { 
  2882. $status = $struct['status']; 
  2883. } else { 
  2884. $status = ''; 
  2885.  
  2886. if ( ! current_user_can( 'moderate_comments' ) && 'approve' !== $status ) { 
  2887. return new IXR_Error( 401, __( 'Invalid comment status.' ) ); 
  2888.  
  2889. $post_id = ''; 
  2890. if ( isset( $struct['post_id'] ) ) { 
  2891. $post_id = absint( $struct['post_id'] ); 
  2892.  
  2893. $post_type = ''; 
  2894. if ( isset( $struct['post_type'] ) ) { 
  2895. $post_type_object = get_post_type_object( $struct['post_type'] ); 
  2896. if ( ! $post_type_object || ! post_type_supports( $post_type_object->name, 'comments' ) ) { 
  2897. return new IXR_Error( 404, __( 'Invalid post type.' ) ); 
  2898. $post_type = $struct['post_type']; 
  2899.  
  2900. $offset = 0; 
  2901. if ( isset( $struct['offset'] ) ) { 
  2902. $offset = absint( $struct['offset'] ); 
  2903.  
  2904. $number = 10; 
  2905. if ( isset( $struct['number'] ) ) { 
  2906. $number = absint( $struct['number'] ); 
  2907.  
  2908. $comments = get_comments( array( 
  2909. 'status' => $status,  
  2910. 'post_id' => $post_id,  
  2911. 'offset' => $offset,  
  2912. 'number' => $number,  
  2913. 'post_type' => $post_type,  
  2914. ) ); 
  2915.  
  2916. $comments_struct = array(); 
  2917. if ( is_array( $comments ) ) { 
  2918. foreach ( $comments as $comment ) { 
  2919. $comments_struct[] = $this->_prepare_comment( $comment ); 
  2920.  
  2921. return $comments_struct; 
  2922.  
  2923. /** 
  2924. * Delete a comment. 
  2925. * By default, the comment will be moved to the trash instead of deleted. 
  2926. * See wp_delete_comment() for more information on this behavior. 
  2927. * @since 2.7.0 
  2928. * @param array $args { 
  2929. * Method arguments. Note: arguments must be ordered as documented. 
  2930. * @type int $blog_id (unused) 
  2931. * @type string $username 
  2932. * @type string $password 
  2933. * @type int $comment_ID 
  2934. * } 
  2935. * @return bool|IXR_Error See wp_delete_comment(). 
  2936. */ 
  2937. public function wp_deleteComment( $args ) { 
  2938. $this->escape($args); 
  2939.  
  2940. $username = $args[1]; 
  2941. $password = $args[2]; 
  2942. $comment_ID = (int) $args[3]; 
  2943.  
  2944. if ( ! $user = $this->login( $username, $password ) ) { 
  2945. return $this->error; 
  2946.  
  2947. if ( ! get_comment( $comment_ID ) ) { 
  2948. return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 
  2949.  
  2950. if ( !current_user_can( 'edit_comment', $comment_ID ) ) { 
  2951. return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); 
  2952.  
  2953. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  2954. do_action( 'xmlrpc_call', 'wp.deleteComment' ); 
  2955.  
  2956. $status = wp_delete_comment( $comment_ID ); 
  2957.  
  2958. if ( $status ) { 
  2959. /** 
  2960. * Fires after a comment has been successfully deleted via XML-RPC. 
  2961. * @since 3.4.0 
  2962. * @param int $comment_ID ID of the deleted comment. 
  2963. * @param array $args An array of arguments to delete the comment. 
  2964. */ 
  2965. do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_ID, $args ); 
  2966.  
  2967. return $status; 
  2968.  
  2969. /** 
  2970. * Edit comment. 
  2971. * Besides the common blog_id (unused), username, and password arguments, it takes a 
  2972. * comment_id integer and a content_struct array as last argument. 
  2973. * The allowed keys in the content_struct array are: 
  2974. * - 'author' 
  2975. * - 'author_url' 
  2976. * - 'author_email' 
  2977. * - 'content' 
  2978. * - 'date_created_gmt' 
  2979. * - 'status'. Common statuses are 'approve', 'hold', 'spam'. See get_comment_statuses() for more details 
  2980. * @since 2.7.0 
  2981. * @param array $args { 
  2982. * Method arguments. Note: arguments must be ordered as documented. 
  2983. * @type int $blog_id (unused) 
  2984. * @type string $username 
  2985. * @type string $password 
  2986. * @type int $comment_ID 
  2987. * @type array $content_struct 
  2988. * } 
  2989. * @return true|IXR_Error True, on success. 
  2990. */ 
  2991. public function wp_editComment( $args ) { 
  2992. $this->escape( $args ); 
  2993.  
  2994. $username = $args[1]; 
  2995. $password = $args[2]; 
  2996. $comment_ID = (int) $args[3]; 
  2997. $content_struct = $args[4]; 
  2998.  
  2999. if ( !$user = $this->login( $username, $password ) ) { 
  3000. return $this->error; 
  3001.  
  3002. if ( ! get_comment( $comment_ID ) ) { 
  3003. return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 
  3004.  
  3005. if ( ! current_user_can( 'edit_comment', $comment_ID ) ) { 
  3006. return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); 
  3007.  
  3008. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3009. do_action( 'xmlrpc_call', 'wp.editComment' ); 
  3010.  
  3011. if ( isset($content_struct['status']) ) { 
  3012. $statuses = get_comment_statuses(); 
  3013. $statuses = array_keys($statuses); 
  3014.  
  3015. if ( ! in_array($content_struct['status'], $statuses) ) 
  3016. return new IXR_Error( 401, __( 'Invalid comment status.' ) ); 
  3017. $comment_approved = $content_struct['status']; 
  3018.  
  3019. // Do some timestamp voodoo 
  3020. if ( !empty( $content_struct['date_created_gmt'] ) ) { 
  3021. // We know this is supposed to be GMT, so we're going to slap that Z on there by force 
  3022. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; 
  3023. $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 
  3024. $comment_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); 
  3025.  
  3026. if ( isset($content_struct['content']) ) 
  3027. $comment_content = $content_struct['content']; 
  3028.  
  3029. if ( isset($content_struct['author']) ) 
  3030. $comment_author = $content_struct['author']; 
  3031.  
  3032. if ( isset($content_struct['author_url']) ) 
  3033. $comment_author_url = $content_struct['author_url']; 
  3034.  
  3035. if ( isset($content_struct['author_email']) ) 
  3036. $comment_author_email = $content_struct['author_email']; 
  3037.  
  3038. // We've got all the data -- post it: 
  3039. $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url'); 
  3040.  
  3041. $result = wp_update_comment($comment); 
  3042. if ( is_wp_error( $result ) ) 
  3043. return new IXR_Error(500, $result->get_error_message()); 
  3044.  
  3045. if ( !$result ) 
  3046. return new IXR_Error(500, __('Sorry, the comment could not be edited.')); 
  3047.  
  3048. /** 
  3049. * Fires after a comment has been successfully updated via XML-RPC. 
  3050. * @since 3.4.0 
  3051. * @param int $comment_ID ID of the updated comment. 
  3052. * @param array $args An array of arguments to update the comment. 
  3053. */ 
  3054. do_action( 'xmlrpc_call_success_wp_editComment', $comment_ID, $args ); 
  3055.  
  3056. return true; 
  3057.  
  3058. /** 
  3059. * Create new comment. 
  3060. * @since 2.7.0 
  3061. * @param array $args { 
  3062. * Method arguments. Note: arguments must be ordered as documented. 
  3063. * @type int $blog_id (unused) 
  3064. * @type string $username 
  3065. * @type string $password 
  3066. * @type string|int $post 
  3067. * @type array $content_struct 
  3068. * } 
  3069. * @return int|IXR_Error See wp_new_comment(). 
  3070. */ 
  3071. public function wp_newComment($args) { 
  3072. $this->escape($args); 
  3073.  
  3074. $username = $args[1]; 
  3075. $password = $args[2]; 
  3076. $post = $args[3]; 
  3077. $content_struct = $args[4]; 
  3078.  
  3079. /** 
  3080. * Filters whether to allow anonymous comments over XML-RPC. 
  3081. * @since 2.7.0 
  3082. * @param bool $allow Whether to allow anonymous commenting via XML-RPC. 
  3083. * Default false. 
  3084. */ 
  3085. $allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false ); 
  3086.  
  3087. $user = $this->login($username, $password); 
  3088.  
  3089. if ( !$user ) { 
  3090. $logged_in = false; 
  3091. if ( $allow_anon && get_option('comment_registration') ) { 
  3092. return new IXR_Error( 403, __( 'You must be registered to comment.' ) ); 
  3093. } elseif ( ! $allow_anon ) { 
  3094. return $this->error; 
  3095. } else { 
  3096. $logged_in = true; 
  3097.  
  3098. if ( is_numeric($post) ) 
  3099. $post_id = absint($post); 
  3100. else 
  3101. $post_id = url_to_postid($post); 
  3102.  
  3103. if ( ! $post_id ) { 
  3104. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3105.  
  3106. if ( ! get_post( $post_id ) ) { 
  3107. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3108.  
  3109. if ( ! comments_open( $post_id ) ) { 
  3110. return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) ); 
  3111.  
  3112. if ( empty( $content_struct['content'] ) ) { 
  3113. return new IXR_Error( 403, __( 'Comment is required.' ) ); 
  3114.  
  3115. $comment = array( 
  3116. 'comment_post_ID' => $post_id,  
  3117. 'comment_content' => $content_struct['content'],  
  3118. ); 
  3119.  
  3120. if ( $logged_in ) { 
  3121. $display_name = $user->display_name; 
  3122. $user_email = $user->user_email; 
  3123. $user_url = $user->user_url; 
  3124.  
  3125. $comment['comment_author'] = $this->escape( $display_name ); 
  3126. $comment['comment_author_email'] = $this->escape( $user_email ); 
  3127. $comment['comment_author_url'] = $this->escape( $user_url ); 
  3128. $comment['user_ID'] = $user->ID; 
  3129. } else { 
  3130. $comment['comment_author'] = ''; 
  3131. if ( isset($content_struct['author']) ) 
  3132. $comment['comment_author'] = $content_struct['author']; 
  3133.  
  3134. $comment['comment_author_email'] = ''; 
  3135. if ( isset($content_struct['author_email']) ) 
  3136. $comment['comment_author_email'] = $content_struct['author_email']; 
  3137.  
  3138. $comment['comment_author_url'] = ''; 
  3139. if ( isset($content_struct['author_url']) ) 
  3140. $comment['comment_author_url'] = $content_struct['author_url']; 
  3141.  
  3142. $comment['user_ID'] = 0; 
  3143.  
  3144. if ( get_option('require_name_email') ) { 
  3145. if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] ) 
  3146. return new IXR_Error( 403, __( 'Comment author name and email are required.' ) ); 
  3147. elseif ( !is_email($comment['comment_author_email']) ) 
  3148. return new IXR_Error( 403, __( 'A valid email address is required.' ) ); 
  3149.  
  3150. $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0; 
  3151.  
  3152. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3153. do_action( 'xmlrpc_call', 'wp.newComment' ); 
  3154.  
  3155. $comment_ID = wp_new_comment( $comment, true ); 
  3156. if ( is_wp_error( $comment_ID ) ) { 
  3157. return new IXR_Error( 403, $comment_ID->get_error_message() ); 
  3158.  
  3159. if ( ! $comment_ID ) { 
  3160. return new IXR_Error( 403, __( 'An unknown error occurred' ) ); 
  3161.  
  3162. /** 
  3163. * Fires after a new comment has been successfully created via XML-RPC. 
  3164. * @since 3.4.0 
  3165. * @param int $comment_ID ID of the new comment. 
  3166. * @param array $args An array of new comment arguments. 
  3167. */ 
  3168. do_action( 'xmlrpc_call_success_wp_newComment', $comment_ID, $args ); 
  3169.  
  3170. return $comment_ID; 
  3171.  
  3172. /** 
  3173. * Retrieve all of the comment status. 
  3174. * @since 2.7.0 
  3175. * @param array $args { 
  3176. * Method arguments. Note: arguments must be ordered as documented. 
  3177. * @type int $blog_id (unused) 
  3178. * @type string $username 
  3179. * @type string $password 
  3180. * } 
  3181. * @return array|IXR_Error 
  3182. */ 
  3183. public function wp_getCommentStatusList( $args ) { 
  3184. $this->escape( $args ); 
  3185.  
  3186. $username = $args[1]; 
  3187. $password = $args[2]; 
  3188.  
  3189. if ( ! $user = $this->login( $username, $password ) ) { 
  3190. return $this->error; 
  3191.  
  3192. if ( ! current_user_can( 'publish_posts' ) ) { 
  3193. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3194.  
  3195. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3196. do_action( 'xmlrpc_call', 'wp.getCommentStatusList' ); 
  3197.  
  3198. return get_comment_statuses(); 
  3199.  
  3200. /** 
  3201. * Retrieve comment count. 
  3202. * @since 2.5.0 
  3203. * @param array $args { 
  3204. * Method arguments. Note: arguments must be ordered as documented. 
  3205. * @type int $blog_id (unused) 
  3206. * @type string $username 
  3207. * @type string $password 
  3208. * @type int $post_id 
  3209. * } 
  3210. * @return array|IXR_Error 
  3211. */ 
  3212. public function wp_getCommentCount( $args ) { 
  3213. $this->escape( $args ); 
  3214.  
  3215. $username = $args[1]; 
  3216. $password = $args[2]; 
  3217. $post_id = (int) $args[3]; 
  3218.  
  3219. if ( ! $user = $this->login( $username, $password ) ) { 
  3220. return $this->error; 
  3221.  
  3222. $post = get_post( $post_id, ARRAY_A ); 
  3223. if ( empty( $post['ID'] ) ) { 
  3224. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3225.  
  3226. if ( ! current_user_can( 'edit_post', $post_id ) ) { 
  3227. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details of this post.' ) ); 
  3228.  
  3229. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3230. do_action( 'xmlrpc_call', 'wp.getCommentCount' ); 
  3231.  
  3232. $count = wp_count_comments( $post_id ); 
  3233.  
  3234. return array( 
  3235. 'approved' => $count->approved,  
  3236. 'awaiting_moderation' => $count->moderated,  
  3237. 'spam' => $count->spam,  
  3238. 'total_comments' => $count->total_comments 
  3239. ); 
  3240.  
  3241. /** 
  3242. * Retrieve post statuses. 
  3243. * @since 2.5.0 
  3244. * @param array $args { 
  3245. * Method arguments. Note: arguments must be ordered as documented. 
  3246. * @type int $blog_id (unused) 
  3247. * @type string $username 
  3248. * @type string $password 
  3249. * } 
  3250. * @return array|IXR_Error 
  3251. */ 
  3252. public function wp_getPostStatusList( $args ) { 
  3253. $this->escape( $args ); 
  3254.  
  3255. $username = $args[1]; 
  3256. $password = $args[2]; 
  3257.  
  3258. if ( !$user = $this->login($username, $password) ) 
  3259. return $this->error; 
  3260.  
  3261. if ( !current_user_can( 'edit_posts' ) ) 
  3262. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3263.  
  3264. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3265. do_action( 'xmlrpc_call', 'wp.getPostStatusList' ); 
  3266.  
  3267. return get_post_statuses(); 
  3268.  
  3269. /** 
  3270. * Retrieve page statuses. 
  3271. * @since 2.5.0 
  3272. * @param array $args { 
  3273. * Method arguments. Note: arguments must be ordered as documented. 
  3274. * @type int $blog_id (unused) 
  3275. * @type string $username 
  3276. * @type string $password 
  3277. * } 
  3278. * @return array|IXR_Error 
  3279. */ 
  3280. public function wp_getPageStatusList( $args ) { 
  3281. $this->escape( $args ); 
  3282.  
  3283. $username = $args[1]; 
  3284. $password = $args[2]; 
  3285.  
  3286. if ( !$user = $this->login($username, $password) ) 
  3287. return $this->error; 
  3288.  
  3289. if ( !current_user_can( 'edit_pages' ) ) 
  3290. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3291.  
  3292. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3293. do_action( 'xmlrpc_call', 'wp.getPageStatusList' ); 
  3294.  
  3295. return get_page_statuses(); 
  3296.  
  3297. /** 
  3298. * Retrieve page templates. 
  3299. * @since 2.6.0 
  3300. * @param array $args { 
  3301. * Method arguments. Note: arguments must be ordered as documented. 
  3302. * @type int $blog_id (unused) 
  3303. * @type string $username 
  3304. * @type string $password 
  3305. * } 
  3306. * @return array|IXR_Error 
  3307. */ 
  3308. public function wp_getPageTemplates( $args ) { 
  3309. $this->escape( $args ); 
  3310.  
  3311. $username = $args[1]; 
  3312. $password = $args[2]; 
  3313.  
  3314. if ( !$user = $this->login($username, $password) ) 
  3315. return $this->error; 
  3316.  
  3317. if ( !current_user_can( 'edit_pages' ) ) 
  3318. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3319.  
  3320. $templates = get_page_templates(); 
  3321. $templates['Default'] = 'default'; 
  3322.  
  3323. return $templates; 
  3324.  
  3325. /** 
  3326. * Retrieve blog options. 
  3327. * @since 2.6.0 
  3328. * @param array $args { 
  3329. * Method arguments. Note: arguments must be ordered as documented. 
  3330. * @type int $blog_id (unused) 
  3331. * @type string $username 
  3332. * @type string $password 
  3333. * @type array $options 
  3334. * } 
  3335. * @return array|IXR_Error 
  3336. */ 
  3337. public function wp_getOptions( $args ) { 
  3338. $this->escape( $args ); 
  3339.  
  3340. $username = $args[1]; 
  3341. $password = $args[2]; 
  3342. $options = isset( $args[3] ) ? (array) $args[3] : array(); 
  3343.  
  3344. if ( !$user = $this->login($username, $password) ) 
  3345. return $this->error; 
  3346.  
  3347. // If no specific options where asked for, return all of them 
  3348. if ( count( $options ) == 0 ) 
  3349. $options = array_keys($this->blog_options); 
  3350.  
  3351. return $this->_getOptions($options); 
  3352.  
  3353. /** 
  3354. * Retrieve blog options value from list. 
  3355. * @since 2.6.0 
  3356. * @param array $options Options to retrieve. 
  3357. * @return array 
  3358. */ 
  3359. public function _getOptions($options) { 
  3360. $data = array(); 
  3361. $can_manage = current_user_can( 'manage_options' ); 
  3362. foreach ( $options as $option ) { 
  3363. if ( array_key_exists( $option, $this->blog_options ) ) { 
  3364. $data[$option] = $this->blog_options[$option]; 
  3365. //Is the value static or dynamic? 
  3366. if ( isset( $data[$option]['option'] ) ) { 
  3367. $data[$option]['value'] = get_option( $data[$option]['option'] ); 
  3368. unset($data[$option]['option']); 
  3369.  
  3370. if ( ! $can_manage ) 
  3371. $data[$option]['readonly'] = true; 
  3372.  
  3373. return $data; 
  3374.  
  3375. /** 
  3376. * Update blog options. 
  3377. * @since 2.6.0 
  3378. * @param array $args { 
  3379. * Method arguments. Note: arguments must be ordered as documented. 
  3380. * @type int $blog_id (unused) 
  3381. * @type string $username 
  3382. * @type string $password 
  3383. * @type array $options 
  3384. * } 
  3385. * @return array|IXR_Error 
  3386. */ 
  3387. public function wp_setOptions( $args ) { 
  3388. $this->escape( $args ); 
  3389.  
  3390. $username = $args[1]; 
  3391. $password = $args[2]; 
  3392. $options = (array) $args[3]; 
  3393.  
  3394. if ( !$user = $this->login($username, $password) ) 
  3395. return $this->error; 
  3396.  
  3397. if ( !current_user_can( 'manage_options' ) ) 
  3398. return new IXR_Error( 403, __( 'Sorry, you are not allowed to update options.' ) ); 
  3399.  
  3400. $option_names = array(); 
  3401. foreach ( $options as $o_name => $o_value ) { 
  3402. $option_names[] = $o_name; 
  3403. if ( !array_key_exists( $o_name, $this->blog_options ) ) 
  3404. continue; 
  3405.  
  3406. if ( $this->blog_options[$o_name]['readonly'] == true ) 
  3407. continue; 
  3408.  
  3409. update_option( $this->blog_options[$o_name]['option'], wp_unslash( $o_value ) ); 
  3410.  
  3411. //Now return the updated values 
  3412. return $this->_getOptions($option_names); 
  3413.  
  3414. /** 
  3415. * Retrieve a media item by ID 
  3416. * @since 3.1.0 
  3417. * @param array $args { 
  3418. * Method arguments. Note: arguments must be ordered as documented. 
  3419. * @type int $blog_id (unused) 
  3420. * @type string $username 
  3421. * @type string $password 
  3422. * @type int $attachment_id 
  3423. * } 
  3424. * @return array|IXR_Error Associative array contains: 
  3425. * - 'date_created_gmt' 
  3426. * - 'parent' 
  3427. * - 'link' 
  3428. * - 'thumbnail' 
  3429. * - 'title' 
  3430. * - 'caption' 
  3431. * - 'description' 
  3432. * - 'metadata' 
  3433. */ 
  3434. public function wp_getMediaItem( $args ) { 
  3435. $this->escape( $args ); 
  3436.  
  3437. $username = $args[1]; 
  3438. $password = $args[2]; 
  3439. $attachment_id = (int) $args[3]; 
  3440.  
  3441. if ( !$user = $this->login($username, $password) ) 
  3442. return $this->error; 
  3443.  
  3444. if ( !current_user_can( 'upload_files' ) ) 
  3445. return new IXR_Error( 403, __( 'Sorry, you are not allowed to upload files.' ) ); 
  3446.  
  3447. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3448. do_action( 'xmlrpc_call', 'wp.getMediaItem' ); 
  3449.  
  3450. if ( ! $attachment = get_post($attachment_id) ) 
  3451. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  3452.  
  3453. return $this->_prepare_media_item( $attachment ); 
  3454.  
  3455. /** 
  3456. * Retrieves a collection of media library items (or attachments) 
  3457. * Besides the common blog_id (unused), username, and password arguments, it takes a filter 
  3458. * array as last argument. 
  3459. * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'. 
  3460. * The defaults are as follows: 
  3461. * - 'number' - Default is 5. Total number of media items to retrieve. 
  3462. * - 'offset' - Default is 0. See WP_Query::query() for more. 
  3463. * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items. 
  3464. * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf') 
  3465. * @since 3.1.0 
  3466. * @param array $args { 
  3467. * Method arguments. Note: arguments must be ordered as documented. 
  3468. * @type int $blog_id (unused) 
  3469. * @type string $username 
  3470. * @type string $password 
  3471. * @type array $struct 
  3472. * } 
  3473. * @return array|IXR_Error Contains a collection of media items. See wp_xmlrpc_server::wp_getMediaItem() for a description of each item contents 
  3474. */ 
  3475. public function wp_getMediaLibrary($args) { 
  3476. $this->escape($args); 
  3477.  
  3478. $username = $args[1]; 
  3479. $password = $args[2]; 
  3480. $struct = isset( $args[3] ) ? $args[3] : array() ; 
  3481.  
  3482. if ( !$user = $this->login($username, $password) ) 
  3483. return $this->error; 
  3484.  
  3485. if ( !current_user_can( 'upload_files' ) ) 
  3486. return new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); 
  3487.  
  3488. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3489. do_action( 'xmlrpc_call', 'wp.getMediaLibrary' ); 
  3490.  
  3491. $parent_id = ( isset($struct['parent_id']) ) ? absint($struct['parent_id']) : '' ; 
  3492. $mime_type = ( isset($struct['mime_type']) ) ? $struct['mime_type'] : '' ; 
  3493. $offset = ( isset($struct['offset']) ) ? absint($struct['offset']) : 0 ; 
  3494. $number = ( isset($struct['number']) ) ? absint($struct['number']) : -1 ; 
  3495.  
  3496. $attachments = get_posts( array('post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type ) ); 
  3497.  
  3498. $attachments_struct = array(); 
  3499.  
  3500. foreach ($attachments as $attachment ) 
  3501. $attachments_struct[] = $this->_prepare_media_item( $attachment ); 
  3502.  
  3503. return $attachments_struct; 
  3504.  
  3505. /** 
  3506. * Retrieves a list of post formats used by the site. 
  3507. * @since 3.1.0 
  3508. * @param array $args { 
  3509. * Method arguments. Note: arguments must be ordered as documented. 
  3510. * @type int $blog_id (unused) 
  3511. * @type string $username 
  3512. * @type string $password 
  3513. * } 
  3514. * @return array|IXR_Error List of post formats, otherwise IXR_Error object. 
  3515. */ 
  3516. public function wp_getPostFormats( $args ) { 
  3517. $this->escape( $args ); 
  3518.  
  3519. $username = $args[1]; 
  3520. $password = $args[2]; 
  3521.  
  3522. if ( !$user = $this->login( $username, $password ) ) 
  3523. return $this->error; 
  3524.  
  3525. if ( !current_user_can( 'edit_posts' ) ) 
  3526. return new IXR_Error( 403, __( 'Sorry, you are not allowed access to details about this site.' ) ); 
  3527.  
  3528. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3529. do_action( 'xmlrpc_call', 'wp.getPostFormats' ); 
  3530.  
  3531. $formats = get_post_format_strings(); 
  3532.  
  3533. // find out if they want a list of currently supports formats 
  3534. if ( isset( $args[3] ) && is_array( $args[3] ) ) { 
  3535. if ( $args[3]['show-supported'] ) { 
  3536. if ( current_theme_supports( 'post-formats' ) ) { 
  3537. $supported = get_theme_support( 'post-formats' ); 
  3538.  
  3539. $data = array(); 
  3540. $data['all'] = $formats; 
  3541. $data['supported'] = $supported[0]; 
  3542.  
  3543. $formats = $data; 
  3544.  
  3545. return $formats; 
  3546.  
  3547. /** 
  3548. * Retrieves a post type 
  3549. * @since 3.4.0 
  3550. * @see get_post_type_object() 
  3551. * @param array $args { 
  3552. * Method arguments. Note: arguments must be ordered as documented. 
  3553. * @type int $blog_id (unused) 
  3554. * @type string $username 
  3555. * @type string $password 
  3556. * @type string $post_type_name 
  3557. * @type array $fields (optional) 
  3558. * } 
  3559. * @return array|IXR_Error Array contains: 
  3560. * - 'labels' 
  3561. * - 'description' 
  3562. * - 'capability_type' 
  3563. * - 'cap' 
  3564. * - 'map_meta_cap' 
  3565. * - 'hierarchical' 
  3566. * - 'menu_position' 
  3567. * - 'taxonomies' 
  3568. * - 'supports' 
  3569. */ 
  3570. public function wp_getPostType( $args ) { 
  3571. if ( ! $this->minimum_args( $args, 4 ) ) 
  3572. return $this->error; 
  3573.  
  3574. $this->escape( $args ); 
  3575.  
  3576. $username = $args[1]; 
  3577. $password = $args[2]; 
  3578. $post_type_name = $args[3]; 
  3579.  
  3580. if ( isset( $args[4] ) ) { 
  3581. $fields = $args[4]; 
  3582. } else { 
  3583. /** 
  3584. * Filters the default query fields used by the given XML-RPC method. 
  3585. * @since 3.4.0 
  3586. * @param array $fields An array of post type query fields for the given method. 
  3587. * @param string $method The method name. 
  3588. */ 
  3589. $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' ); 
  3590.  
  3591. if ( !$user = $this->login( $username, $password ) ) 
  3592. return $this->error; 
  3593.  
  3594. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3595. do_action( 'xmlrpc_call', 'wp.getPostType' ); 
  3596.  
  3597. if ( ! post_type_exists( $post_type_name ) ) 
  3598. return new IXR_Error( 403, __( 'Invalid post type.' ) ); 
  3599.  
  3600. $post_type = get_post_type_object( $post_type_name ); 
  3601.  
  3602. if ( ! current_user_can( $post_type->cap->edit_posts ) ) 
  3603. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); 
  3604.  
  3605. return $this->_prepare_post_type( $post_type, $fields ); 
  3606.  
  3607. /** 
  3608. * Retrieves a post types 
  3609. * @since 3.4.0 
  3610. * @see get_post_types() 
  3611. * @param array $args { 
  3612. * Method arguments. Note: arguments must be ordered as documented. 
  3613. * @type int $blog_id (unused) 
  3614. * @type string $username 
  3615. * @type string $password 
  3616. * @type array $filter (optional) 
  3617. * @type array $fields (optional) 
  3618. * } 
  3619. * @return array|IXR_Error 
  3620. */ 
  3621. public function wp_getPostTypes( $args ) { 
  3622. if ( ! $this->minimum_args( $args, 3 ) ) 
  3623. return $this->error; 
  3624.  
  3625. $this->escape( $args ); 
  3626.  
  3627. $username = $args[1]; 
  3628. $password = $args[2]; 
  3629. $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); 
  3630.  
  3631. if ( isset( $args[4] ) ) { 
  3632. $fields = $args[4]; 
  3633. } else { 
  3634. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3635. $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' ); 
  3636.  
  3637. if ( ! $user = $this->login( $username, $password ) ) 
  3638. return $this->error; 
  3639.  
  3640. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3641. do_action( 'xmlrpc_call', 'wp.getPostTypes' ); 
  3642.  
  3643. $post_types = get_post_types( $filter, 'objects' ); 
  3644.  
  3645. $struct = array(); 
  3646.  
  3647. foreach ( $post_types as $post_type ) { 
  3648. if ( ! current_user_can( $post_type->cap->edit_posts ) ) 
  3649. continue; 
  3650.  
  3651. $struct[$post_type->name] = $this->_prepare_post_type( $post_type, $fields ); 
  3652.  
  3653. return $struct; 
  3654.  
  3655. /** 
  3656. * Retrieve revisions for a specific post. 
  3657. * @since 3.5.0 
  3658. * The optional $fields parameter specifies what fields will be included 
  3659. * in the response array. 
  3660. * @uses wp_get_post_revisions() 
  3661. * @see wp_getPost() for more on $fields 
  3662. * @param array $args { 
  3663. * Method arguments. Note: arguments must be ordered as documented. 
  3664. * @type int $blog_id (unused) 
  3665. * @type string $username 
  3666. * @type string $password 
  3667. * @type int $post_id 
  3668. * @type array $fields (optional) 
  3669. * } 
  3670. * @return array|IXR_Error contains a collection of posts. 
  3671. */ 
  3672. public function wp_getRevisions( $args ) { 
  3673. if ( ! $this->minimum_args( $args, 4 ) ) 
  3674. return $this->error; 
  3675.  
  3676. $this->escape( $args ); 
  3677.  
  3678. $username = $args[1]; 
  3679. $password = $args[2]; 
  3680. $post_id = (int) $args[3]; 
  3681.  
  3682. if ( isset( $args[4] ) ) { 
  3683. $fields = $args[4]; 
  3684. } else { 
  3685. /** 
  3686. * Filters the default revision query fields used by the given XML-RPC method. 
  3687. * @since 3.5.0 
  3688. * @param array $field An array of revision query fields. 
  3689. * @param string $method The method name. 
  3690. */ 
  3691. $fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' ); 
  3692.  
  3693. if ( ! $user = $this->login( $username, $password ) ) 
  3694. return $this->error; 
  3695.  
  3696. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3697. do_action( 'xmlrpc_call', 'wp.getRevisions' ); 
  3698.  
  3699. if ( ! $post = get_post( $post_id ) ) 
  3700. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3701.  
  3702. if ( ! current_user_can( 'edit_post', $post_id ) ) 
  3703. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  3704.  
  3705. // Check if revisions are enabled. 
  3706. if ( ! wp_revisions_enabled( $post ) ) 
  3707. return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); 
  3708.  
  3709. $revisions = wp_get_post_revisions( $post_id ); 
  3710.  
  3711. if ( ! $revisions ) 
  3712. return array(); 
  3713.  
  3714. $struct = array(); 
  3715.  
  3716. foreach ( $revisions as $revision ) { 
  3717. if ( ! current_user_can( 'read_post', $revision->ID ) ) 
  3718. continue; 
  3719.  
  3720. // Skip autosaves 
  3721. if ( wp_is_post_autosave( $revision ) ) 
  3722. continue; 
  3723.  
  3724. $struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields ); 
  3725.  
  3726. return $struct; 
  3727.  
  3728. /** 
  3729. * Restore a post revision 
  3730. * @since 3.5.0 
  3731. * @uses wp_restore_post_revision() 
  3732. * @param array $args { 
  3733. * Method arguments. Note: arguments must be ordered as documented. 
  3734. * @type int $blog_id (unused) 
  3735. * @type string $username 
  3736. * @type string $password 
  3737. * @type int $revision_id 
  3738. * } 
  3739. * @return bool|IXR_Error false if there was an error restoring, true if success. 
  3740. */ 
  3741. public function wp_restoreRevision( $args ) { 
  3742. if ( ! $this->minimum_args( $args, 3 ) ) 
  3743. return $this->error; 
  3744.  
  3745. $this->escape( $args ); 
  3746.  
  3747. $username = $args[1]; 
  3748. $password = $args[2]; 
  3749. $revision_id = (int) $args[3]; 
  3750.  
  3751. if ( ! $user = $this->login( $username, $password ) ) 
  3752. return $this->error; 
  3753.  
  3754. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3755. do_action( 'xmlrpc_call', 'wp.restoreRevision' ); 
  3756.  
  3757. if ( ! $revision = wp_get_post_revision( $revision_id ) ) 
  3758. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3759.  
  3760. if ( wp_is_post_autosave( $revision ) ) 
  3761. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3762.  
  3763. if ( ! $post = get_post( $revision->post_parent ) ) 
  3764. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3765.  
  3766. if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) 
  3767. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  3768.  
  3769. // Check if revisions are disabled. 
  3770. if ( ! wp_revisions_enabled( $post ) ) 
  3771. return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); 
  3772.  
  3773. $post = wp_restore_post_revision( $revision_id ); 
  3774.  
  3775. return (bool) $post; 
  3776.  
  3777. /** Blogger API functions. 
  3778. * specs on http://plant.blogger.com/api and https://groups.yahoo.com/group/bloggerDev/ 
  3779. */ 
  3780.  
  3781. /** 
  3782. * Retrieve blogs that user owns. 
  3783. * Will make more sense once we support multiple blogs. 
  3784. * @since 1.5.0 
  3785. * @param array $args { 
  3786. * Method arguments. Note: arguments must be ordered as documented. 
  3787. * @type int $blog_id (unused) 
  3788. * @type string $username 
  3789. * @type string $password 
  3790. * } 
  3791. * @return array|IXR_Error 
  3792. */ 
  3793. public function blogger_getUsersBlogs($args) { 
  3794. if ( ! $this->minimum_args( $args, 3 ) ) { 
  3795. return $this->error; 
  3796.  
  3797. if ( is_multisite() ) { 
  3798. return $this->_multisite_getUsersBlogs($args); 
  3799.  
  3800. $this->escape($args); 
  3801.  
  3802. $username = $args[1]; 
  3803. $password = $args[2]; 
  3804.  
  3805. if ( !$user = $this->login($username, $password) ) 
  3806. return $this->error; 
  3807.  
  3808. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3809. do_action( 'xmlrpc_call', 'blogger.getUsersBlogs' ); 
  3810.  
  3811. $is_admin = current_user_can('manage_options'); 
  3812.  
  3813. $struct = array( 
  3814. 'isAdmin' => $is_admin,  
  3815. 'url' => get_option('home') . '/',  
  3816. 'blogid' => '1',  
  3817. 'blogName' => get_option('blogname'),  
  3818. 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),  
  3819. ); 
  3820.  
  3821. return array($struct); 
  3822.  
  3823. /** 
  3824. * Private function for retrieving a users blogs for multisite setups 
  3825. * @since 3.0.0 
  3826. * @access protected 
  3827. * @param array $args { 
  3828. * Method arguments. Note: arguments must be ordered as documented. 
  3829. * @type string $username Username. 
  3830. * @type string $password Password. 
  3831. * } 
  3832. * @return array|IXR_Error 
  3833. */ 
  3834. protected function _multisite_getUsersBlogs( $args ) { 
  3835. $current_blog = get_site(); 
  3836.  
  3837. $domain = $current_blog->domain; 
  3838. $path = $current_blog->path . 'xmlrpc.php'; 
  3839.  
  3840. $rpc = new IXR_Client( set_url_scheme( "http://{$domain}{$path}" ) ); 
  3841. $rpc->query('wp.getUsersBlogs', $args[1], $args[2]); 
  3842. $blogs = $rpc->getResponse(); 
  3843.  
  3844. if ( isset($blogs['faultCode']) ) 
  3845. return new IXR_Error($blogs['faultCode'], $blogs['faultString']); 
  3846.  
  3847. if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { 
  3848. return $blogs; 
  3849. } else { 
  3850. foreach ( (array) $blogs as $blog ) { 
  3851. if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) ) 
  3852. return array($blog); 
  3853. return array(); 
  3854.  
  3855. /** 
  3856. * Retrieve user's data. 
  3857. * Gives your client some info about you, so you don't have to. 
  3858. * @since 1.5.0 
  3859. * @param array $args { 
  3860. * Method arguments. Note: arguments must be ordered as documented. 
  3861. * @type int $blog_id (unused) 
  3862. * @type string $username 
  3863. * @type string $password 
  3864. * } 
  3865. * @return array|IXR_Error 
  3866. */ 
  3867. public function blogger_getUserInfo( $args ) { 
  3868. $this->escape( $args ); 
  3869.  
  3870. $username = $args[1]; 
  3871. $password = $args[2]; 
  3872.  
  3873. if ( !$user = $this->login($username, $password) ) 
  3874. return $this->error; 
  3875.  
  3876. if ( !current_user_can( 'edit_posts' ) ) 
  3877. return new IXR_Error( 401, __( 'Sorry, you are not allowed to access user data on this site.' ) ); 
  3878.  
  3879. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3880. do_action( 'xmlrpc_call', 'blogger.getUserInfo' ); 
  3881.  
  3882. $struct = array( 
  3883. 'nickname' => $user->nickname,  
  3884. 'userid' => $user->ID,  
  3885. 'url' => $user->user_url,  
  3886. 'lastname' => $user->last_name,  
  3887. 'firstname' => $user->first_name 
  3888. ); 
  3889.  
  3890. return $struct; 
  3891.  
  3892. /** 
  3893. * Retrieve post. 
  3894. * @since 1.5.0 
  3895. * @param array $args { 
  3896. * Method arguments. Note: arguments must be ordered as documented. 
  3897. * @type int $blog_id (unused) 
  3898. * @type int $post_ID 
  3899. * @type string $username 
  3900. * @type string $password 
  3901. * } 
  3902. * @return array|IXR_Error 
  3903. */ 
  3904. public function blogger_getPost( $args ) { 
  3905. $this->escape( $args ); 
  3906.  
  3907. $post_ID = (int) $args[1]; 
  3908. $username = $args[2]; 
  3909. $password = $args[3]; 
  3910.  
  3911. if ( !$user = $this->login($username, $password) ) 
  3912. return $this->error; 
  3913.  
  3914. $post_data = get_post($post_ID, ARRAY_A); 
  3915. if ( ! $post_data ) 
  3916. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  3917.  
  3918. if ( !current_user_can( 'edit_post', $post_ID ) ) 
  3919. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  3920.  
  3921. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3922. do_action( 'xmlrpc_call', 'blogger.getPost' ); 
  3923.  
  3924. $categories = implode(', ', wp_get_post_categories($post_ID)); 
  3925.  
  3926. $content = '<title>'.wp_unslash($post_data['post_title']).'</title>'; 
  3927. $content .= '<category>'.$categories.'</category>'; 
  3928. $content .= wp_unslash($post_data['post_content']); 
  3929.  
  3930. $struct = array( 
  3931. 'userid' => $post_data['post_author'],  
  3932. 'dateCreated' => $this->_convert_date( $post_data['post_date'] ),  
  3933. 'content' => $content,  
  3934. 'postid' => (string) $post_data['ID'] 
  3935. ); 
  3936.  
  3937. return $struct; 
  3938.  
  3939. /** 
  3940. * Retrieve list of recent posts. 
  3941. * @since 1.5.0 
  3942. * @param array $args { 
  3943. * Method arguments. Note: arguments must be ordered as documented. 
  3944. * @type string $appkey (unused) 
  3945. * @type int $blog_id (unused) 
  3946. * @type string $username 
  3947. * @type string $password 
  3948. * @type int $numberposts (optional) 
  3949. * } 
  3950. * @return array|IXR_Error 
  3951. */ 
  3952. public function blogger_getRecentPosts( $args ) { 
  3953.  
  3954. $this->escape($args); 
  3955.  
  3956. // $args[0] = appkey - ignored 
  3957. $username = $args[2]; 
  3958. $password = $args[3]; 
  3959. if ( isset( $args[4] ) ) 
  3960. $query = array( 'numberposts' => absint( $args[4] ) ); 
  3961. else 
  3962. $query = array(); 
  3963.  
  3964. if ( !$user = $this->login($username, $password) ) 
  3965. return $this->error; 
  3966.  
  3967. if ( ! current_user_can( 'edit_posts' ) ) 
  3968. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  3969.  
  3970. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  3971. do_action( 'xmlrpc_call', 'blogger.getRecentPosts' ); 
  3972.  
  3973. $posts_list = wp_get_recent_posts( $query ); 
  3974.  
  3975. if ( !$posts_list ) { 
  3976. $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 
  3977. return $this->error; 
  3978.  
  3979. $recent_posts = array(); 
  3980. foreach ($posts_list as $entry) { 
  3981. if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 
  3982. continue; 
  3983.  
  3984. $post_date = $this->_convert_date( $entry['post_date'] ); 
  3985. $categories = implode(', ', wp_get_post_categories($entry['ID'])); 
  3986.  
  3987. $content = '<title>'.wp_unslash($entry['post_title']).'</title>'; 
  3988. $content .= '<category>'.$categories.'</category>'; 
  3989. $content .= wp_unslash($entry['post_content']); 
  3990.  
  3991. $recent_posts[] = array( 
  3992. 'userid' => $entry['post_author'],  
  3993. 'dateCreated' => $post_date,  
  3994. 'content' => $content,  
  3995. 'postid' => (string) $entry['ID'],  
  3996. ); 
  3997.  
  3998. return $recent_posts; 
  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_getTemplate($args) { 
  4008. return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) ); 
  4009.  
  4010. /** 
  4011. * Deprecated. 
  4012. * @since 1.5.0 
  4013. * @deprecated 3.5.0 
  4014. * @param array $args Unused. 
  4015. * @return IXR_Error Error object. 
  4016. */ 
  4017. public function blogger_setTemplate($args) { 
  4018. return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) ); 
  4019.  
  4020. /** 
  4021. * Creates new post. 
  4022. * @since 1.5.0 
  4023. * @param array $args { 
  4024. * Method arguments. Note: arguments must be ordered as documented. 
  4025. * @type string $appkey (unused) 
  4026. * @type int $blog_id (unused) 
  4027. * @type string $username 
  4028. * @type string $password 
  4029. * @type string $content 
  4030. * @type string $publish 
  4031. * } 
  4032. * @return int|IXR_Error 
  4033. */ 
  4034. public function blogger_newPost( $args ) { 
  4035. $this->escape( $args ); 
  4036.  
  4037. $username = $args[2]; 
  4038. $password = $args[3]; 
  4039. $content = $args[4]; 
  4040. $publish = $args[5]; 
  4041.  
  4042. if ( !$user = $this->login($username, $password) ) 
  4043. return $this->error; 
  4044.  
  4045. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4046. do_action( 'xmlrpc_call', 'blogger.newPost' ); 
  4047.  
  4048. $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 
  4049. if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || !current_user_can($cap) ) 
  4050. return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.')); 
  4051.  
  4052. $post_status = ($publish) ? 'publish' : 'draft'; 
  4053.  
  4054. $post_author = $user->ID; 
  4055.  
  4056. $post_title = xmlrpc_getposttitle($content); 
  4057. $post_category = xmlrpc_getpostcategory($content); 
  4058. $post_content = xmlrpc_removepostdata($content); 
  4059.  
  4060. $post_date = current_time('mysql'); 
  4061. $post_date_gmt = current_time('mysql', 1); 
  4062.  
  4063. $post_data = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); 
  4064.  
  4065. $post_ID = wp_insert_post($post_data); 
  4066. if ( is_wp_error( $post_ID ) ) 
  4067. return new IXR_Error(500, $post_ID->get_error_message()); 
  4068.  
  4069. if ( !$post_ID ) 
  4070. return new IXR_Error(500, __('Sorry, your entry could not be posted.')); 
  4071.  
  4072. $this->attach_uploads( $post_ID, $post_content ); 
  4073.  
  4074. /** 
  4075. * Fires after a new post has been successfully created via the XML-RPC Blogger API. 
  4076. * @since 3.4.0 
  4077. * @param int $post_ID ID of the new post. 
  4078. * @param array $args An array of new post arguments. 
  4079. */ 
  4080. do_action( 'xmlrpc_call_success_blogger_newPost', $post_ID, $args ); 
  4081.  
  4082. return $post_ID; 
  4083.  
  4084. /** 
  4085. * Edit a post. 
  4086. * @since 1.5.0 
  4087. * @param array $args { 
  4088. * Method arguments. Note: arguments must be ordered as documented. 
  4089. * @type int $blog_id (unused) 
  4090. * @type int $post_ID 
  4091. * @type string $username 
  4092. * @type string $password 
  4093. * @type string $content 
  4094. * @type bool $publish 
  4095. * } 
  4096. * @return true|IXR_Error true when done. 
  4097. */ 
  4098. public function blogger_editPost( $args ) { 
  4099.  
  4100. $this->escape($args); 
  4101.  
  4102. $post_ID = (int) $args[1]; 
  4103. $username = $args[2]; 
  4104. $password = $args[3]; 
  4105. $content = $args[4]; 
  4106. $publish = $args[5]; 
  4107.  
  4108. if ( ! $user = $this->login( $username, $password ) ) { 
  4109. return $this->error; 
  4110.  
  4111. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4112. do_action( 'xmlrpc_call', 'blogger.editPost' ); 
  4113.  
  4114. $actual_post = get_post( $post_ID, ARRAY_A ); 
  4115.  
  4116. if ( ! $actual_post || $actual_post['post_type'] != 'post' ) { 
  4117. return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); 
  4118.  
  4119. $this->escape($actual_post); 
  4120.  
  4121. if ( ! current_user_can( 'edit_post', $post_ID ) ) { 
  4122. return new IXR_Error(401, __('Sorry, you are not allowed to edit this post.')); 
  4123. if ( 'publish' == $actual_post['post_status'] && ! current_user_can( 'publish_posts' ) ) { 
  4124. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); 
  4125.  
  4126. $postdata = array(); 
  4127. $postdata['ID'] = $actual_post['ID']; 
  4128. $postdata['post_content'] = xmlrpc_removepostdata( $content ); 
  4129. $postdata['post_title'] = xmlrpc_getposttitle( $content ); 
  4130. $postdata['post_category'] = xmlrpc_getpostcategory( $content ); 
  4131. $postdata['post_status'] = $actual_post['post_status']; 
  4132. $postdata['post_excerpt'] = $actual_post['post_excerpt']; 
  4133. $postdata['post_status'] = $publish ? 'publish' : 'draft'; 
  4134.  
  4135. $result = wp_update_post( $postdata ); 
  4136.  
  4137. if ( ! $result ) { 
  4138. return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); 
  4139. $this->attach_uploads( $actual_post['ID'], $postdata['post_content'] ); 
  4140.  
  4141. /** 
  4142. * Fires after a post has been successfully updated via the XML-RPC Blogger API. 
  4143. * @since 3.4.0 
  4144. * @param int $post_ID ID of the updated post. 
  4145. * @param array $args An array of arguments for the post to edit. 
  4146. */ 
  4147. do_action( 'xmlrpc_call_success_blogger_editPost', $post_ID, $args ); 
  4148.  
  4149. return true; 
  4150.  
  4151. /** 
  4152. * Remove a post. 
  4153. * @since 1.5.0 
  4154. * @param array $args { 
  4155. * Method arguments. Note: arguments must be ordered as documented. 
  4156. * @type int $blog_id (unused) 
  4157. * @type int $post_ID 
  4158. * @type string $username 
  4159. * @type string $password 
  4160. * } 
  4161. * @return true|IXR_Error True when post is deleted. 
  4162. */ 
  4163. public function blogger_deletePost( $args ) { 
  4164. $this->escape( $args ); 
  4165.  
  4166. $post_ID = (int) $args[1]; 
  4167. $username = $args[2]; 
  4168. $password = $args[3]; 
  4169.  
  4170. if ( !$user = $this->login($username, $password) ) 
  4171. return $this->error; 
  4172.  
  4173. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4174. do_action( 'xmlrpc_call', 'blogger.deletePost' ); 
  4175.  
  4176. $actual_post = get_post( $post_ID, ARRAY_A ); 
  4177.  
  4178. if ( ! $actual_post || $actual_post['post_type'] != 'post' ) { 
  4179. return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); 
  4180.  
  4181. if ( ! current_user_can( 'delete_post', $post_ID ) ) { 
  4182. return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); 
  4183.  
  4184. $result = wp_delete_post( $post_ID ); 
  4185.  
  4186. if ( ! $result ) { 
  4187. return new IXR_Error( 500, __( 'The post cannot be deleted.' ) ); 
  4188.  
  4189. /** 
  4190. * Fires after a post has been successfully deleted via the XML-RPC Blogger API. 
  4191. * @since 3.4.0 
  4192. * @param int $post_ID ID of the deleted post. 
  4193. * @param array $args An array of arguments to delete the post. 
  4194. */ 
  4195. do_action( 'xmlrpc_call_success_blogger_deletePost', $post_ID, $args ); 
  4196.  
  4197. return true; 
  4198.  
  4199. /** MetaWeblog API functions 
  4200. * specs on wherever Dave Winer wants them to be 
  4201. */ 
  4202.  
  4203. /** 
  4204. * Create a new post. 
  4205. * The 'content_struct' argument must contain: 
  4206. * - title 
  4207. * - description 
  4208. * - mt_excerpt 
  4209. * - mt_text_more 
  4210. * - mt_keywords 
  4211. * - mt_tb_ping_urls 
  4212. * - categories 
  4213. * Also, it can optionally contain: 
  4214. * - wp_slug 
  4215. * - wp_password 
  4216. * - wp_page_parent_id 
  4217. * - wp_page_order 
  4218. * - wp_author_id 
  4219. * - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending' 
  4220. * - mt_allow_comments - can be 'open' or 'closed' 
  4221. * - mt_allow_pings - can be 'open' or 'closed' 
  4222. * - date_created_gmt 
  4223. * - dateCreated 
  4224. * - wp_post_thumbnail 
  4225. * @since 1.5.0 
  4226. * @param array $args { 
  4227. * Method arguments. Note: arguments must be ordered as documented. 
  4228. * @type int $blog_id (unused) 
  4229. * @type string $username 
  4230. * @type string $password 
  4231. * @type array $content_struct 
  4232. * @type int $publish 
  4233. * } 
  4234. * @return int|IXR_Error 
  4235. */ 
  4236. public function mw_newPost($args) { 
  4237. $this->escape($args); 
  4238.  
  4239. $username = $args[1]; 
  4240. $password = $args[2]; 
  4241. $content_struct = $args[3]; 
  4242. $publish = isset( $args[4] ) ? $args[4] : 0; 
  4243.  
  4244. if ( !$user = $this->login($username, $password) ) 
  4245. return $this->error; 
  4246.  
  4247. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4248. do_action( 'xmlrpc_call', 'metaWeblog.newPost' ); 
  4249.  
  4250. $page_template = ''; 
  4251. if ( !empty( $content_struct['post_type'] ) ) { 
  4252. if ( $content_struct['post_type'] == 'page' ) { 
  4253. if ( $publish ) 
  4254. $cap = 'publish_pages'; 
  4255. elseif ( isset( $content_struct['page_status'] ) && 'publish' == $content_struct['page_status'] ) 
  4256. $cap = 'publish_pages'; 
  4257. else 
  4258. $cap = 'edit_pages'; 
  4259. $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); 
  4260. $post_type = 'page'; 
  4261. if ( !empty( $content_struct['wp_page_template'] ) ) 
  4262. $page_template = $content_struct['wp_page_template']; 
  4263. } elseif ( $content_struct['post_type'] == 'post' ) { 
  4264. if ( $publish ) 
  4265. $cap = 'publish_posts'; 
  4266. elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'] ) 
  4267. $cap = 'publish_posts'; 
  4268. else 
  4269. $cap = 'edit_posts'; 
  4270. $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 
  4271. $post_type = 'post'; 
  4272. } else { 
  4273. // No other post_type values are allowed here 
  4274. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4275. } else { 
  4276. if ( $publish ) 
  4277. $cap = 'publish_posts'; 
  4278. elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status']) 
  4279. $cap = 'publish_posts'; 
  4280. else 
  4281. $cap = 'edit_posts'; 
  4282. $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 
  4283. $post_type = 'post'; 
  4284.  
  4285. if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) ) 
  4286. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) ); 
  4287. if ( !current_user_can( $cap ) ) 
  4288. return new IXR_Error( 401, $error_message ); 
  4289.  
  4290. // Check for a valid post format if one was given 
  4291. if ( isset( $content_struct['wp_post_format'] ) ) { 
  4292. $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); 
  4293. if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { 
  4294. return new IXR_Error( 404, __( 'Invalid post format.' ) ); 
  4295.  
  4296. // Let WordPress generate the post_name (slug) unless 
  4297. // one has been provided. 
  4298. $post_name = ""; 
  4299. if ( isset($content_struct['wp_slug']) ) 
  4300. $post_name = $content_struct['wp_slug']; 
  4301.  
  4302. // Only use a password if one was given. 
  4303. if ( isset($content_struct['wp_password']) ) 
  4304. $post_password = $content_struct['wp_password']; 
  4305.  
  4306. // Only set a post parent if one was provided. 
  4307. if ( isset($content_struct['wp_page_parent_id']) ) 
  4308. $post_parent = $content_struct['wp_page_parent_id']; 
  4309.  
  4310. // Only set the menu_order if it was provided. 
  4311. if ( isset($content_struct['wp_page_order']) ) 
  4312. $menu_order = $content_struct['wp_page_order']; 
  4313.  
  4314. $post_author = $user->ID; 
  4315.  
  4316. // If an author id was provided then use it instead. 
  4317. if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) { 
  4318. switch ( $post_type ) { 
  4319. case "post": 
  4320. if ( !current_user_can( 'edit_others_posts' ) ) 
  4321. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); 
  4322. break; 
  4323. case "page": 
  4324. if ( !current_user_can( 'edit_others_pages' ) ) 
  4325. return new IXR_Error( 401, __( 'Sorry, you are not allowed to create pages as this user.' ) ); 
  4326. break; 
  4327. default: 
  4328. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4329. $author = get_userdata( $content_struct['wp_author_id'] ); 
  4330. if ( ! $author ) 
  4331. return new IXR_Error( 404, __( 'Invalid author ID.' ) ); 
  4332. $post_author = $content_struct['wp_author_id']; 
  4333.  
  4334. $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null; 
  4335. $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null; 
  4336.  
  4337. $post_status = $publish ? 'publish' : 'draft'; 
  4338.  
  4339. if ( isset( $content_struct["{$post_type}_status"] ) ) { 
  4340. switch ( $content_struct["{$post_type}_status"] ) { 
  4341. case 'draft': 
  4342. case 'pending': 
  4343. case 'private': 
  4344. case 'publish': 
  4345. $post_status = $content_struct["{$post_type}_status"]; 
  4346. break; 
  4347. default: 
  4348. $post_status = $publish ? 'publish' : 'draft'; 
  4349. break; 
  4350.  
  4351. $post_excerpt = isset($content_struct['mt_excerpt']) ? $content_struct['mt_excerpt'] : null; 
  4352. $post_more = isset($content_struct['mt_text_more']) ? $content_struct['mt_text_more'] : null; 
  4353.  
  4354. $tags_input = isset($content_struct['mt_keywords']) ? $content_struct['mt_keywords'] : null; 
  4355.  
  4356. if ( isset($content_struct['mt_allow_comments']) ) { 
  4357. if ( !is_numeric($content_struct['mt_allow_comments']) ) { 
  4358. switch ( $content_struct['mt_allow_comments'] ) { 
  4359. case 'closed': 
  4360. $comment_status = 'closed'; 
  4361. break; 
  4362. case 'open': 
  4363. $comment_status = 'open'; 
  4364. break; 
  4365. default: 
  4366. $comment_status = get_default_comment_status( $post_type ); 
  4367. break; 
  4368. } else { 
  4369. switch ( (int) $content_struct['mt_allow_comments'] ) { 
  4370. case 0: 
  4371. case 2: 
  4372. $comment_status = 'closed'; 
  4373. break; 
  4374. case 1: 
  4375. $comment_status = 'open'; 
  4376. break; 
  4377. default: 
  4378. $comment_status = get_default_comment_status( $post_type ); 
  4379. break; 
  4380. } else { 
  4381. $comment_status = get_default_comment_status( $post_type ); 
  4382.  
  4383. if ( isset($content_struct['mt_allow_pings']) ) { 
  4384. if ( !is_numeric($content_struct['mt_allow_pings']) ) { 
  4385. switch ( $content_struct['mt_allow_pings'] ) { 
  4386. case 'closed': 
  4387. $ping_status = 'closed'; 
  4388. break; 
  4389. case 'open': 
  4390. $ping_status = 'open'; 
  4391. break; 
  4392. default: 
  4393. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4394. break; 
  4395. } else { 
  4396. switch ( (int) $content_struct['mt_allow_pings'] ) { 
  4397. case 0: 
  4398. $ping_status = 'closed'; 
  4399. break; 
  4400. case 1: 
  4401. $ping_status = 'open'; 
  4402. break; 
  4403. default: 
  4404. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4405. break; 
  4406. } else { 
  4407. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4408.  
  4409. if ( $post_more ) 
  4410. $post_content = $post_content . '<!--more-->' . $post_more; 
  4411.  
  4412. $to_ping = null; 
  4413. if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { 
  4414. $to_ping = $content_struct['mt_tb_ping_urls']; 
  4415. if ( is_array($to_ping) ) 
  4416. $to_ping = implode(' ', $to_ping); 
  4417.  
  4418. // Do some timestamp voodoo 
  4419. if ( !empty( $content_struct['date_created_gmt'] ) ) 
  4420. // We know this is supposed to be GMT, so we're going to slap that Z on there by force 
  4421. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; 
  4422. elseif ( !empty( $content_struct['dateCreated']) ) 
  4423. $dateCreated = $content_struct['dateCreated']->getIso(); 
  4424.  
  4425. if ( !empty( $dateCreated ) ) { 
  4426. $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 
  4427. $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); 
  4428. } else { 
  4429. $post_date = ''; 
  4430. $post_date_gmt = ''; 
  4431.  
  4432. $post_category = array(); 
  4433. if ( isset( $content_struct['categories'] ) ) { 
  4434. $catnames = $content_struct['categories']; 
  4435.  
  4436. if ( is_array($catnames) ) { 
  4437. foreach ($catnames as $cat) { 
  4438. $post_category[] = get_cat_ID($cat); 
  4439.  
  4440. $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'); 
  4441.  
  4442. $post_ID = $postdata['ID'] = get_default_post_to_edit( $post_type, true )->ID; 
  4443.  
  4444. // Only posts can be sticky 
  4445. if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 
  4446. $data = $postdata; 
  4447. $data['sticky'] = $content_struct['sticky']; 
  4448. $error = $this->_toggle_sticky( $data ); 
  4449. if ( $error ) { 
  4450. return $error; 
  4451.  
  4452. if ( isset($content_struct['custom_fields']) ) 
  4453. $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 
  4454.  
  4455. if ( isset ( $content_struct['wp_post_thumbnail'] ) ) { 
  4456. if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) 
  4457. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  4458.  
  4459. unset( $content_struct['wp_post_thumbnail'] ); 
  4460.  
  4461. // Handle enclosures 
  4462. $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null; 
  4463. $this->add_enclosure_if_new($post_ID, $thisEnclosure); 
  4464.  
  4465. $this->attach_uploads( $post_ID, $post_content ); 
  4466.  
  4467. // Handle post formats if assigned, value is validated earlier 
  4468. // in this function 
  4469. if ( isset( $content_struct['wp_post_format'] ) ) 
  4470. set_post_format( $post_ID, $content_struct['wp_post_format'] ); 
  4471.  
  4472. $post_ID = wp_insert_post( $postdata, true ); 
  4473. if ( is_wp_error( $post_ID ) ) 
  4474. return new IXR_Error(500, $post_ID->get_error_message()); 
  4475.  
  4476. if ( !$post_ID ) 
  4477. return new IXR_Error(500, __('Sorry, your entry could not be posted.')); 
  4478.  
  4479. /** 
  4480. * Fires after a new post has been successfully created via the XML-RPC MovableType API. 
  4481. * @since 3.4.0 
  4482. * @param int $post_ID ID of the new post. 
  4483. * @param array $args An array of arguments to create the new post. 
  4484. */ 
  4485. do_action( 'xmlrpc_call_success_mw_newPost', $post_ID, $args ); 
  4486.  
  4487. return strval($post_ID); 
  4488.  
  4489. /** 
  4490. * Adds an enclosure to a post if it's new. 
  4491. * @since 2.8.0 
  4492. * @param integer $post_ID Post ID. 
  4493. * @param array $enclosure Enclosure data. 
  4494. */ 
  4495. public function add_enclosure_if_new( $post_ID, $enclosure ) { 
  4496. if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { 
  4497. $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] . "\n"; 
  4498. $found = false; 
  4499. if ( $enclosures = get_post_meta( $post_ID, 'enclosure' ) ) { 
  4500. foreach ( $enclosures as $enc ) { 
  4501. // This method used to omit the trailing new line. #23219 
  4502. if ( rtrim( $enc, "\n" ) == rtrim( $encstring, "\n" ) ) { 
  4503. $found = true; 
  4504. break; 
  4505. if ( ! $found ) 
  4506. add_post_meta( $post_ID, 'enclosure', $encstring ); 
  4507.  
  4508. /** 
  4509. * Attach upload to a post. 
  4510. * @since 2.1.0 
  4511. * @global wpdb $wpdb WordPress database abstraction object. 
  4512. * @param int $post_ID Post ID. 
  4513. * @param string $post_content Post Content for attachment. 
  4514. */ 
  4515. public function attach_uploads( $post_ID, $post_content ) { 
  4516. global $wpdb; 
  4517.  
  4518. // find any unattached files 
  4519. $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); 
  4520. if ( is_array( $attachments ) ) { 
  4521. foreach ( $attachments as $file ) { 
  4522. if ( ! empty( $file->guid ) && strpos( $post_content, $file->guid ) !== false ) 
  4523. $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) ); 
  4524.  
  4525. /** 
  4526. * Edit a post. 
  4527. * @since 1.5.0 
  4528. * @param array $args { 
  4529. * Method arguments. Note: arguments must be ordered as documented. 
  4530. * @type int $blog_id (unused) 
  4531. * @type string $username 
  4532. * @type string $password 
  4533. * @type array $content_struct 
  4534. * @type int $publish 
  4535. * } 
  4536. * @return bool|IXR_Error True on success. 
  4537. */ 
  4538. public function mw_editPost( $args ) { 
  4539. $this->escape( $args ); 
  4540.  
  4541. $post_ID = (int) $args[0]; 
  4542. $username = $args[1]; 
  4543. $password = $args[2]; 
  4544. $content_struct = $args[3]; 
  4545. $publish = isset( $args[4] ) ? $args[4] : 0; 
  4546.  
  4547. if ( ! $user = $this->login($username, $password) ) 
  4548. return $this->error; 
  4549.  
  4550. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4551. do_action( 'xmlrpc_call', 'metaWeblog.editPost' ); 
  4552.  
  4553. $postdata = get_post( $post_ID, ARRAY_A ); 
  4554.  
  4555. /** 
  4556. * If there is no post data for the give post id, stop now and return an error. 
  4557. * Otherwise a new post will be created (which was the old behavior). 
  4558. */ 
  4559. if ( ! $postdata || empty( $postdata[ 'ID' ] ) ) 
  4560. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4561.  
  4562. if ( ! current_user_can( 'edit_post', $post_ID ) ) 
  4563. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  4564.  
  4565. // Use wp.editPost to edit post types other than post and page. 
  4566. if ( ! in_array( $postdata[ 'post_type' ], array( 'post', 'page' ) ) ) 
  4567. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4568.  
  4569. // Thwart attempt to change the post type. 
  4570. if ( ! empty( $content_struct[ 'post_type' ] ) && ( $content_struct['post_type'] != $postdata[ 'post_type' ] ) ) 
  4571. return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); 
  4572.  
  4573. // Check for a valid post format if one was given 
  4574. if ( isset( $content_struct['wp_post_format'] ) ) { 
  4575. $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); 
  4576. if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { 
  4577. return new IXR_Error( 404, __( 'Invalid post format.' ) ); 
  4578.  
  4579. $this->escape($postdata); 
  4580.  
  4581. $ID = $postdata['ID']; 
  4582. $post_content = $postdata['post_content']; 
  4583. $post_title = $postdata['post_title']; 
  4584. $post_excerpt = $postdata['post_excerpt']; 
  4585. $post_password = $postdata['post_password']; 
  4586. $post_parent = $postdata['post_parent']; 
  4587. $post_type = $postdata['post_type']; 
  4588. $menu_order = $postdata['menu_order']; 
  4589.  
  4590. // Let WordPress manage slug if none was provided. 
  4591. $post_name = $postdata['post_name']; 
  4592. if ( isset($content_struct['wp_slug']) ) 
  4593. $post_name = $content_struct['wp_slug']; 
  4594.  
  4595. // Only use a password if one was given. 
  4596. if ( isset($content_struct['wp_password']) ) 
  4597. $post_password = $content_struct['wp_password']; 
  4598.  
  4599. // Only set a post parent if one was given. 
  4600. if ( isset($content_struct['wp_page_parent_id']) ) 
  4601. $post_parent = $content_struct['wp_page_parent_id']; 
  4602.  
  4603. // Only set the menu_order if it was given. 
  4604. if ( isset($content_struct['wp_page_order']) ) 
  4605. $menu_order = $content_struct['wp_page_order']; 
  4606.  
  4607. $page_template = null; 
  4608. if ( ! empty( $content_struct['wp_page_template'] ) && 'page' == $post_type ) 
  4609. $page_template = $content_struct['wp_page_template']; 
  4610.  
  4611. $post_author = $postdata['post_author']; 
  4612.  
  4613. // Only set the post_author if one is set. 
  4614. if ( isset( $content_struct['wp_author_id'] ) ) { 
  4615. // Check permissions if attempting to switch author to or from another user. 
  4616. if ( $user->ID != $content_struct['wp_author_id'] || $user->ID != $post_author ) { 
  4617. switch ( $post_type ) { 
  4618. case 'post': 
  4619. if ( ! current_user_can( 'edit_others_posts' ) ) { 
  4620. return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the post author as this user.' ) ); 
  4621. break; 
  4622. case 'page': 
  4623. if ( ! current_user_can( 'edit_others_pages' ) ) { 
  4624. return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the page author as this user.' ) ); 
  4625. break; 
  4626. default: 
  4627. return new IXR_Error( 401, __( 'Invalid post type.' ) ); 
  4628. $post_author = $content_struct['wp_author_id']; 
  4629.  
  4630. if ( isset($content_struct['mt_allow_comments']) ) { 
  4631. if ( !is_numeric($content_struct['mt_allow_comments']) ) { 
  4632. switch ( $content_struct['mt_allow_comments'] ) { 
  4633. case 'closed': 
  4634. $comment_status = 'closed'; 
  4635. break; 
  4636. case 'open': 
  4637. $comment_status = 'open'; 
  4638. break; 
  4639. default: 
  4640. $comment_status = get_default_comment_status( $post_type ); 
  4641. break; 
  4642. } else { 
  4643. switch ( (int) $content_struct['mt_allow_comments'] ) { 
  4644. case 0: 
  4645. case 2: 
  4646. $comment_status = 'closed'; 
  4647. break; 
  4648. case 1: 
  4649. $comment_status = 'open'; 
  4650. break; 
  4651. default: 
  4652. $comment_status = get_default_comment_status( $post_type ); 
  4653. break; 
  4654.  
  4655. if ( isset($content_struct['mt_allow_pings']) ) { 
  4656. if ( !is_numeric($content_struct['mt_allow_pings']) ) { 
  4657. switch ( $content_struct['mt_allow_pings'] ) { 
  4658. case 'closed': 
  4659. $ping_status = 'closed'; 
  4660. break; 
  4661. case 'open': 
  4662. $ping_status = 'open'; 
  4663. break; 
  4664. default: 
  4665. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4666. break; 
  4667. } else { 
  4668. switch ( (int) $content_struct["mt_allow_pings"] ) { 
  4669. case 0: 
  4670. $ping_status = 'closed'; 
  4671. break; 
  4672. case 1: 
  4673. $ping_status = 'open'; 
  4674. break; 
  4675. default: 
  4676. $ping_status = get_default_comment_status( $post_type, 'pingback' ); 
  4677. break; 
  4678.  
  4679. if ( isset( $content_struct['title'] ) ) 
  4680. $post_title = $content_struct['title']; 
  4681.  
  4682. if ( isset( $content_struct['description'] ) ) 
  4683. $post_content = $content_struct['description']; 
  4684.  
  4685. $post_category = array(); 
  4686. if ( isset( $content_struct['categories'] ) ) { 
  4687. $catnames = $content_struct['categories']; 
  4688. if ( is_array($catnames) ) { 
  4689. foreach ($catnames as $cat) { 
  4690. $post_category[] = get_cat_ID($cat); 
  4691.  
  4692. if ( isset( $content_struct['mt_excerpt'] ) ) 
  4693. $post_excerpt = $content_struct['mt_excerpt']; 
  4694.  
  4695. $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null; 
  4696.  
  4697. $post_status = $publish ? 'publish' : 'draft'; 
  4698. if ( isset( $content_struct["{$post_type}_status"] ) ) { 
  4699. switch( $content_struct["{$post_type}_status"] ) { 
  4700. case 'draft': 
  4701. case 'pending': 
  4702. case 'private': 
  4703. case 'publish': 
  4704. $post_status = $content_struct["{$post_type}_status"]; 
  4705. break; 
  4706. default: 
  4707. $post_status = $publish ? 'publish' : 'draft'; 
  4708. break; 
  4709.  
  4710. $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null; 
  4711.  
  4712. if ( 'publish' == $post_status || 'private' == $post_status ) { 
  4713. if ( 'page' == $post_type && ! current_user_can( 'publish_pages' ) ) { 
  4714. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this page.' ) ); 
  4715. } elseif ( ! current_user_can( 'publish_posts' ) ) { 
  4716. return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); 
  4717.  
  4718. if ( $post_more ) 
  4719. $post_content = $post_content . "<!--more-->" . $post_more; 
  4720.  
  4721. $to_ping = null; 
  4722. if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { 
  4723. $to_ping = $content_struct['mt_tb_ping_urls']; 
  4724. if ( is_array($to_ping) ) 
  4725. $to_ping = implode(' ', $to_ping); 
  4726.  
  4727. // Do some timestamp voodoo. 
  4728. if ( !empty( $content_struct['date_created_gmt'] ) ) 
  4729. // We know this is supposed to be GMT, so we're going to slap that Z on there by force. 
  4730. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; 
  4731. elseif ( !empty( $content_struct['dateCreated']) ) 
  4732. $dateCreated = $content_struct['dateCreated']->getIso(); 
  4733.  
  4734. // Default to not flagging the post date to be edited unless it's intentional. 
  4735. $edit_date = false; 
  4736.  
  4737. if ( !empty( $dateCreated ) ) { 
  4738. $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 
  4739. $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); 
  4740.  
  4741. // Flag the post date to be edited. 
  4742. $edit_date = true; 
  4743. } else { 
  4744. $post_date = $postdata['post_date']; 
  4745. $post_date_gmt = $postdata['post_date_gmt']; 
  4746.  
  4747. // We've got all the data -- post it. 
  4748. $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'); 
  4749.  
  4750. $result = wp_update_post($newpost, true); 
  4751. if ( is_wp_error( $result ) ) 
  4752. return new IXR_Error(500, $result->get_error_message()); 
  4753.  
  4754. if ( !$result ) 
  4755. return new IXR_Error(500, __('Sorry, your entry could not be edited.')); 
  4756.  
  4757. // Only posts can be sticky 
  4758. if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 
  4759. $data = $newpost; 
  4760. $data['sticky'] = $content_struct['sticky']; 
  4761. $data['post_type'] = 'post'; 
  4762. $error = $this->_toggle_sticky( $data, true ); 
  4763. if ( $error ) { 
  4764. return $error; 
  4765.  
  4766. if ( isset($content_struct['custom_fields']) ) 
  4767. $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 
  4768.  
  4769. if ( isset ( $content_struct['wp_post_thumbnail'] ) ) { 
  4770.  
  4771. // Empty value deletes, non-empty value adds/updates. 
  4772. if ( empty( $content_struct['wp_post_thumbnail'] ) ) { 
  4773. delete_post_thumbnail( $post_ID ); 
  4774. } else { 
  4775. if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) 
  4776. return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); 
  4777. unset( $content_struct['wp_post_thumbnail'] ); 
  4778.  
  4779. // Handle enclosures. 
  4780. $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null; 
  4781. $this->add_enclosure_if_new($post_ID, $thisEnclosure); 
  4782.  
  4783. $this->attach_uploads( $ID, $post_content ); 
  4784.  
  4785. // Handle post formats if assigned, validation is handled earlier in this function. 
  4786. if ( isset( $content_struct['wp_post_format'] ) ) 
  4787. set_post_format( $post_ID, $content_struct['wp_post_format'] ); 
  4788.  
  4789. /** 
  4790. * Fires after a post has been successfully updated via the XML-RPC MovableType API. 
  4791. * @since 3.4.0 
  4792. * @param int $post_ID ID of the updated post. 
  4793. * @param array $args An array of arguments to update the post. 
  4794. */ 
  4795. do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args ); 
  4796.  
  4797. return true; 
  4798.  
  4799. /** 
  4800. * Retrieve post. 
  4801. * @since 1.5.0 
  4802. * @param array $args { 
  4803. * Method arguments. Note: arguments must be ordered as documented. 
  4804. * @type int $blog_id (unused) 
  4805. * @type int $post_ID 
  4806. * @type string $username 
  4807. * @type string $password 
  4808. * } 
  4809. * @return array|IXR_Error 
  4810. */ 
  4811. public function mw_getPost( $args ) { 
  4812. $this->escape( $args ); 
  4813.  
  4814. $post_ID = (int) $args[0]; 
  4815. $username = $args[1]; 
  4816. $password = $args[2]; 
  4817.  
  4818. if ( !$user = $this->login($username, $password) ) 
  4819. return $this->error; 
  4820.  
  4821. $postdata = get_post($post_ID, ARRAY_A); 
  4822. if ( ! $postdata ) 
  4823. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  4824.  
  4825. if ( !current_user_can( 'edit_post', $post_ID ) ) 
  4826. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  4827.  
  4828. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4829. do_action( 'xmlrpc_call', 'metaWeblog.getPost' ); 
  4830.  
  4831. if ($postdata['post_date'] != '') { 
  4832. $post_date = $this->_convert_date( $postdata['post_date'] ); 
  4833. $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] ); 
  4834. $post_modified = $this->_convert_date( $postdata['post_modified'] ); 
  4835. $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] ); 
  4836.  
  4837. $categories = array(); 
  4838. $catids = wp_get_post_categories($post_ID); 
  4839. foreach ($catids as $catid) 
  4840. $categories[] = get_cat_name($catid); 
  4841.  
  4842. $tagnames = array(); 
  4843. $tags = wp_get_post_tags( $post_ID ); 
  4844. if ( !empty( $tags ) ) { 
  4845. foreach ( $tags as $tag ) 
  4846. $tagnames[] = $tag->name; 
  4847. $tagnames = implode( ', ', $tagnames ); 
  4848. } else { 
  4849. $tagnames = ''; 
  4850.  
  4851. $post = get_extended($postdata['post_content']); 
  4852. $link = get_permalink($postdata['ID']); 
  4853.  
  4854. // Get the author info. 
  4855. $author = get_userdata($postdata['post_author']); 
  4856.  
  4857. $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; 
  4858. $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; 
  4859.  
  4860. // Consider future posts as published 
  4861. if ( $postdata['post_status'] === 'future' ) 
  4862. $postdata['post_status'] = 'publish'; 
  4863.  
  4864. // Get post format 
  4865. $post_format = get_post_format( $post_ID ); 
  4866. if ( empty( $post_format ) ) 
  4867. $post_format = 'standard'; 
  4868.  
  4869. $sticky = false; 
  4870. if ( is_sticky( $post_ID ) ) 
  4871. $sticky = true; 
  4872.  
  4873. $enclosure = array(); 
  4874. foreach ( (array) get_post_custom($post_ID) as $key => $val) { 
  4875. if ($key == 'enclosure') { 
  4876. foreach ( (array) $val as $enc ) { 
  4877. $encdata = explode("\n", $enc); 
  4878. $enclosure['url'] = trim(htmlspecialchars($encdata[0])); 
  4879. $enclosure['length'] = (int) trim($encdata[1]); 
  4880. $enclosure['type'] = trim($encdata[2]); 
  4881. break 2; 
  4882.  
  4883. $resp = array( 
  4884. 'dateCreated' => $post_date,  
  4885. 'userid' => $postdata['post_author'],  
  4886. 'postid' => $postdata['ID'],  
  4887. 'description' => $post['main'],  
  4888. 'title' => $postdata['post_title'],  
  4889. 'link' => $link,  
  4890. 'permaLink' => $link,  
  4891. // commented out because no other tool seems to use this 
  4892. // 'content' => $entry['post_content'],  
  4893. 'categories' => $categories,  
  4894. 'mt_excerpt' => $postdata['post_excerpt'],  
  4895. 'mt_text_more' => $post['extended'],  
  4896. 'wp_more_text' => $post['more_text'],  
  4897. 'mt_allow_comments' => $allow_comments,  
  4898. 'mt_allow_pings' => $allow_pings,  
  4899. 'mt_keywords' => $tagnames,  
  4900. 'wp_slug' => $postdata['post_name'],  
  4901. 'wp_password' => $postdata['post_password'],  
  4902. 'wp_author_id' => (string) $author->ID,  
  4903. 'wp_author_display_name' => $author->display_name,  
  4904. 'date_created_gmt' => $post_date_gmt,  
  4905. 'post_status' => $postdata['post_status'],  
  4906. 'custom_fields' => $this->get_custom_fields($post_ID),  
  4907. 'wp_post_format' => $post_format,  
  4908. 'sticky' => $sticky,  
  4909. 'date_modified' => $post_modified,  
  4910. 'date_modified_gmt' => $post_modified_gmt 
  4911. ); 
  4912.  
  4913. if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure; 
  4914.  
  4915. $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] ); 
  4916.  
  4917. return $resp; 
  4918. } else { 
  4919. return new IXR_Error(404, __('Sorry, no such post.')); 
  4920.  
  4921. /** 
  4922. * Retrieve list of recent posts. 
  4923. * @since 1.5.0 
  4924. * @param array $args { 
  4925. * Method arguments. Note: arguments must be ordered as documented. 
  4926. * @type int $blog_id (unused) 
  4927. * @type string $username 
  4928. * @type string $password 
  4929. * @type int $numberposts 
  4930. * } 
  4931. * @return array|IXR_Error 
  4932. */ 
  4933. public function mw_getRecentPosts( $args ) { 
  4934. $this->escape( $args ); 
  4935.  
  4936. $username = $args[1]; 
  4937. $password = $args[2]; 
  4938. if ( isset( $args[3] ) ) 
  4939. $query = array( 'numberposts' => absint( $args[3] ) ); 
  4940. else 
  4941. $query = array(); 
  4942.  
  4943. if ( !$user = $this->login($username, $password) ) 
  4944. return $this->error; 
  4945.  
  4946. if ( ! current_user_can( 'edit_posts' ) ) 
  4947. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); 
  4948.  
  4949. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  4950. do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts' ); 
  4951.  
  4952. $posts_list = wp_get_recent_posts( $query ); 
  4953.  
  4954. if ( !$posts_list ) 
  4955. return array(); 
  4956.  
  4957. $recent_posts = array(); 
  4958. foreach ($posts_list as $entry) { 
  4959. if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 
  4960. continue; 
  4961.  
  4962. $post_date = $this->_convert_date( $entry['post_date'] ); 
  4963. $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); 
  4964. $post_modified = $this->_convert_date( $entry['post_modified'] ); 
  4965. $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] ); 
  4966.  
  4967. $categories = array(); 
  4968. $catids = wp_get_post_categories($entry['ID']); 
  4969. foreach ( $catids as $catid ) 
  4970. $categories[] = get_cat_name($catid); 
  4971.  
  4972. $tagnames = array(); 
  4973. $tags = wp_get_post_tags( $entry['ID'] ); 
  4974. if ( !empty( $tags ) ) { 
  4975. foreach ( $tags as $tag ) { 
  4976. $tagnames[] = $tag->name; 
  4977. $tagnames = implode( ', ', $tagnames ); 
  4978. } else { 
  4979. $tagnames = ''; 
  4980.  
  4981. $post = get_extended($entry['post_content']); 
  4982. $link = get_permalink($entry['ID']); 
  4983.  
  4984. // Get the post author info. 
  4985. $author = get_userdata($entry['post_author']); 
  4986.  
  4987. $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; 
  4988. $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; 
  4989.  
  4990. // Consider future posts as published 
  4991. if ( $entry['post_status'] === 'future' ) 
  4992. $entry['post_status'] = 'publish'; 
  4993.  
  4994. // Get post format 
  4995. $post_format = get_post_format( $entry['ID'] ); 
  4996. if ( empty( $post_format ) ) 
  4997. $post_format = 'standard'; 
  4998.  
  4999. $recent_posts[] = array( 
  5000. 'dateCreated' => $post_date,  
  5001. 'userid' => $entry['post_author'],  
  5002. 'postid' => (string) $entry['ID'],  
  5003. 'description' => $post['main'],  
  5004. 'title' => $entry['post_title'],  
  5005. 'link' => $link,  
  5006. 'permaLink' => $link,  
  5007. // commented out because no other tool seems to use this 
  5008. // 'content' => $entry['post_content'],  
  5009. 'categories' => $categories,  
  5010. 'mt_excerpt' => $entry['post_excerpt'],  
  5011. 'mt_text_more' => $post['extended'],  
  5012. 'wp_more_text' => $post['more_text'],  
  5013. 'mt_allow_comments' => $allow_comments,  
  5014. 'mt_allow_pings' => $allow_pings,  
  5015. 'mt_keywords' => $tagnames,  
  5016. 'wp_slug' => $entry['post_name'],  
  5017. 'wp_password' => $entry['post_password'],  
  5018. 'wp_author_id' => (string) $author->ID,  
  5019. 'wp_author_display_name' => $author->display_name,  
  5020. 'date_created_gmt' => $post_date_gmt,  
  5021. 'post_status' => $entry['post_status'],  
  5022. 'custom_fields' => $this->get_custom_fields($entry['ID']),  
  5023. 'wp_post_format' => $post_format,  
  5024. 'date_modified' => $post_modified,  
  5025. 'date_modified_gmt' => $post_modified_gmt,  
  5026. 'sticky' => ( $entry['post_type'] === 'post' && is_sticky( $entry['ID'] ) ),  
  5027. 'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ) 
  5028. ); 
  5029.  
  5030. return $recent_posts; 
  5031.  
  5032. /** 
  5033. * Retrieve the list of categories on a given blog. 
  5034. * @since 1.5.0 
  5035. * @param array $args { 
  5036. * Method arguments. Note: arguments must be ordered as documented. 
  5037. * @type int $blog_id (unused) 
  5038. * @type string $username 
  5039. * @type string $password 
  5040. * } 
  5041. * @return array|IXR_Error 
  5042. */ 
  5043. public function mw_getCategories( $args ) { 
  5044. $this->escape( $args ); 
  5045.  
  5046. $username = $args[1]; 
  5047. $password = $args[2]; 
  5048.  
  5049. if ( !$user = $this->login($username, $password) ) 
  5050. return $this->error; 
  5051.  
  5052. if ( !current_user_can( 'edit_posts' ) ) 
  5053. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 
  5054.  
  5055. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5056. do_action( 'xmlrpc_call', 'metaWeblog.getCategories' ); 
  5057.  
  5058. $categories_struct = array(); 
  5059.  
  5060. if ( $cats = get_categories(array('get' => 'all')) ) { 
  5061. foreach ( $cats as $cat ) { 
  5062. $struct = array(); 
  5063. $struct['categoryId'] = $cat->term_id; 
  5064. $struct['parentId'] = $cat->parent; 
  5065. $struct['description'] = $cat->name; 
  5066. $struct['categoryDescription'] = $cat->description; 
  5067. $struct['categoryName'] = $cat->name; 
  5068. $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id)); 
  5069. $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2')); 
  5070.  
  5071. $categories_struct[] = $struct; 
  5072.  
  5073. return $categories_struct; 
  5074.  
  5075. /** 
  5076. * Uploads a file, following your settings. 
  5077. * Adapted from a patch by Johann Richard. 
  5078. * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ 
  5079. * @since 1.5.0 
  5080. * @global wpdb $wpdb WordPress database abstraction object. 
  5081. * @param array $args { 
  5082. * Method arguments. Note: arguments must be ordered as documented. 
  5083. * @type int $blog_id (unused) 
  5084. * @type string $username 
  5085. * @type string $password 
  5086. * @type array $data 
  5087. * } 
  5088. * @return array|IXR_Error 
  5089. */ 
  5090. public function mw_newMediaObject( $args ) { 
  5091. global $wpdb; 
  5092.  
  5093. $username = $this->escape( $args[1] ); 
  5094. $password = $this->escape( $args[2] ); 
  5095. $data = $args[3]; 
  5096.  
  5097. $name = sanitize_file_name( $data['name'] ); 
  5098. $type = $data['type']; 
  5099. $bits = $data['bits']; 
  5100.  
  5101. if ( !$user = $this->login($username, $password) ) 
  5102. return $this->error; 
  5103.  
  5104. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5105. do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject' ); 
  5106.  
  5107. if ( !current_user_can('upload_files') ) { 
  5108. $this->error = new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); 
  5109. return $this->error; 
  5110.  
  5111. if ( is_multisite() && upload_is_user_over_quota( false ) ) { 
  5112. $this->error = new IXR_Error( 401, __( 'Sorry, you have used your space allocation.' ) ); 
  5113. return $this->error; 
  5114.  
  5115. /** 
  5116. * Filters whether to preempt the XML-RPC media upload. 
  5117. * Passing a truthy value will effectively short-circuit the media upload,  
  5118. * returning that value as a 500 error instead. 
  5119. * @since 2.1.0 
  5120. * @param bool $error Whether to pre-empt the media upload. Default false. 
  5121. */ 
  5122. if ( $upload_err = apply_filters( 'pre_upload_error', false ) ) { 
  5123. return new IXR_Error( 500, $upload_err ); 
  5124.  
  5125. $upload = wp_upload_bits($name, null, $bits); 
  5126. if ( ! empty($upload['error']) ) { 
  5127. /** translators: 1: file name, 2: error message */ 
  5128. $errorString = sprintf( __( 'Could not write file %1$s (%2$s).' ), $name, $upload['error'] ); 
  5129. return new IXR_Error( 500, $errorString ); 
  5130. // Construct the attachment array 
  5131. $post_id = 0; 
  5132. if ( ! empty( $data['post_id'] ) ) { 
  5133. $post_id = (int) $data['post_id']; 
  5134.  
  5135. if ( ! current_user_can( 'edit_post', $post_id ) ) 
  5136. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  5137. $attachment = array( 
  5138. 'post_title' => $name,  
  5139. 'post_content' => '',  
  5140. 'post_type' => 'attachment',  
  5141. 'post_parent' => $post_id,  
  5142. 'post_mime_type' => $type,  
  5143. 'guid' => $upload[ 'url' ] 
  5144. ); 
  5145.  
  5146. // Save the data 
  5147. $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); 
  5148. wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 
  5149.  
  5150. /** 
  5151. * Fires after a new attachment has been added via the XML-RPC MovableType API. 
  5152. * @since 3.4.0 
  5153. * @param int $id ID of the new attachment. 
  5154. * @param array $args An array of arguments to add the attachment. 
  5155. */ 
  5156. do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args ); 
  5157.  
  5158. $struct = $this->_prepare_media_item( get_post( $id ) ); 
  5159.  
  5160. // Deprecated values 
  5161. $struct['id'] = $struct['attachment_id']; 
  5162. $struct['file'] = $struct['title']; 
  5163. $struct['url'] = $struct['link']; 
  5164.  
  5165. return $struct; 
  5166.  
  5167. /** MovableType API functions 
  5168. * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html 
  5169. */ 
  5170.  
  5171. /** 
  5172. * Retrieve the post titles of recent posts. 
  5173. * @since 1.5.0 
  5174. * @param array $args { 
  5175. * Method arguments. Note: arguments must be ordered as documented. 
  5176. * @type int $blog_id (unused) 
  5177. * @type string $username 
  5178. * @type string $password 
  5179. * @type int $numberposts 
  5180. * } 
  5181. * @return array|IXR_Error 
  5182. */ 
  5183. public function mt_getRecentPostTitles( $args ) { 
  5184. $this->escape( $args ); 
  5185.  
  5186. $username = $args[1]; 
  5187. $password = $args[2]; 
  5188. if ( isset( $args[3] ) ) 
  5189. $query = array( 'numberposts' => absint( $args[3] ) ); 
  5190. else 
  5191. $query = array(); 
  5192.  
  5193. if ( !$user = $this->login($username, $password) ) 
  5194. return $this->error; 
  5195.  
  5196. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5197. do_action( 'xmlrpc_call', 'mt.getRecentPostTitles' ); 
  5198.  
  5199. $posts_list = wp_get_recent_posts( $query ); 
  5200.  
  5201. if ( !$posts_list ) { 
  5202. $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 
  5203. return $this->error; 
  5204.  
  5205. $recent_posts = array(); 
  5206.  
  5207. foreach ($posts_list as $entry) { 
  5208. if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 
  5209. continue; 
  5210.  
  5211. $post_date = $this->_convert_date( $entry['post_date'] ); 
  5212. $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); 
  5213.  
  5214. $recent_posts[] = array( 
  5215. 'dateCreated' => $post_date,  
  5216. 'userid' => $entry['post_author'],  
  5217. 'postid' => (string) $entry['ID'],  
  5218. 'title' => $entry['post_title'],  
  5219. 'post_status' => $entry['post_status'],  
  5220. 'date_created_gmt' => $post_date_gmt 
  5221. ); 
  5222.  
  5223. return $recent_posts; 
  5224.  
  5225. /** 
  5226. * Retrieve list of all categories on blog. 
  5227. * @since 1.5.0 
  5228. * @param array $args { 
  5229. * Method arguments. Note: arguments must be ordered as documented. 
  5230. * @type int $blog_id (unused) 
  5231. * @type string $username 
  5232. * @type string $password 
  5233. * } 
  5234. * @return array|IXR_Error 
  5235. */ 
  5236. public function mt_getCategoryList( $args ) { 
  5237. $this->escape( $args ); 
  5238.  
  5239. $username = $args[1]; 
  5240. $password = $args[2]; 
  5241.  
  5242. if ( !$user = $this->login($username, $password) ) 
  5243. return $this->error; 
  5244.  
  5245. if ( !current_user_can( 'edit_posts' ) ) 
  5246. return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 
  5247.  
  5248. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5249. do_action( 'xmlrpc_call', 'mt.getCategoryList' ); 
  5250.  
  5251. $categories_struct = array(); 
  5252.  
  5253. if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) { 
  5254. foreach ( $cats as $cat ) { 
  5255. $struct = array(); 
  5256. $struct['categoryId'] = $cat->term_id; 
  5257. $struct['categoryName'] = $cat->name; 
  5258.  
  5259. $categories_struct[] = $struct; 
  5260.  
  5261. return $categories_struct; 
  5262.  
  5263. /** 
  5264. * Retrieve post categories. 
  5265. * @since 1.5.0 
  5266. * @param array $args { 
  5267. * Method arguments. Note: arguments must be ordered as documented. 
  5268. * @type int $post_ID 
  5269. * @type string $username 
  5270. * @type string $password 
  5271. * } 
  5272. * @return array|IXR_Error 
  5273. */ 
  5274. public function mt_getPostCategories( $args ) { 
  5275. $this->escape( $args ); 
  5276.  
  5277. $post_ID = (int) $args[0]; 
  5278. $username = $args[1]; 
  5279. $password = $args[2]; 
  5280.  
  5281. if ( !$user = $this->login($username, $password) ) 
  5282. return $this->error; 
  5283.  
  5284. if ( ! get_post( $post_ID ) ) 
  5285. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5286.  
  5287. if ( !current_user_can( 'edit_post', $post_ID ) ) 
  5288. return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); 
  5289.  
  5290. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5291. do_action( 'xmlrpc_call', 'mt.getPostCategories' ); 
  5292.  
  5293. $categories = array(); 
  5294. $catids = wp_get_post_categories(intval($post_ID)); 
  5295. // first listed category will be the primary category 
  5296. $isPrimary = true; 
  5297. foreach ( $catids as $catid ) { 
  5298. $categories[] = array( 
  5299. 'categoryName' => get_cat_name($catid),  
  5300. 'categoryId' => (string) $catid,  
  5301. 'isPrimary' => $isPrimary 
  5302. ); 
  5303. $isPrimary = false; 
  5304.  
  5305. return $categories; 
  5306.  
  5307. /** 
  5308. * Sets categories for a post. 
  5309. * @since 1.5.0 
  5310. * @param array $args { 
  5311. * Method arguments. Note: arguments must be ordered as documented. 
  5312. * @type int $post_ID 
  5313. * @type string $username 
  5314. * @type string $password 
  5315. * @type array $categories 
  5316. * } 
  5317. * @return true|IXR_Error True on success. 
  5318. */ 
  5319. public function mt_setPostCategories( $args ) { 
  5320. $this->escape( $args ); 
  5321.  
  5322. $post_ID = (int) $args[0]; 
  5323. $username = $args[1]; 
  5324. $password = $args[2]; 
  5325. $categories = $args[3]; 
  5326.  
  5327. if ( !$user = $this->login($username, $password) ) 
  5328. return $this->error; 
  5329.  
  5330. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5331. do_action( 'xmlrpc_call', 'mt.setPostCategories' ); 
  5332.  
  5333. if ( ! get_post( $post_ID ) ) 
  5334. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5335.  
  5336. if ( !current_user_can('edit_post', $post_ID) ) 
  5337. return new IXR_Error(401, __('Sorry, you are not allowed to edit this post.')); 
  5338.  
  5339. $catids = array(); 
  5340. foreach ( $categories as $cat ) { 
  5341. $catids[] = $cat['categoryId']; 
  5342.  
  5343. wp_set_post_categories($post_ID, $catids); 
  5344.  
  5345. return true; 
  5346.  
  5347. /** 
  5348. * Retrieve an array of methods supported by this server. 
  5349. * @since 1.5.0 
  5350. * @return array 
  5351. */ 
  5352. public function mt_supportedMethods() { 
  5353. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5354. do_action( 'xmlrpc_call', 'mt.supportedMethods' ); 
  5355.  
  5356. return array_keys( $this->methods ); 
  5357.  
  5358. /** 
  5359. * Retrieve an empty array because we don't support per-post text filters. 
  5360. * @since 1.5.0 
  5361. */ 
  5362. public function mt_supportedTextFilters() { 
  5363. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5364. do_action( 'xmlrpc_call', 'mt.supportedTextFilters' ); 
  5365.  
  5366. /** 
  5367. * Filters the MoveableType text filters list for XML-RPC. 
  5368. * @since 2.2.0 
  5369. * @param array $filters An array of text filters. 
  5370. */ 
  5371. return apply_filters( 'xmlrpc_text_filters', array() ); 
  5372.  
  5373. /** 
  5374. * Retrieve trackbacks sent to a given post. 
  5375. * @since 1.5.0 
  5376. * @global wpdb $wpdb WordPress database abstraction object. 
  5377. * @param int $post_ID 
  5378. * @return array|IXR_Error 
  5379. */ 
  5380. public function mt_getTrackbackPings( $post_ID ) { 
  5381. global $wpdb; 
  5382.  
  5383. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5384. do_action( 'xmlrpc_call', 'mt.getTrackbackPings' ); 
  5385.  
  5386. $actual_post = get_post($post_ID, ARRAY_A); 
  5387.  
  5388. if ( !$actual_post ) 
  5389. return new IXR_Error(404, __('Sorry, no such post.')); 
  5390.  
  5391. $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) ); 
  5392.  
  5393. if ( !$comments ) 
  5394. return array(); 
  5395.  
  5396. $trackback_pings = array(); 
  5397. foreach ( $comments as $comment ) { 
  5398. if ( 'trackback' == $comment->comment_type ) { 
  5399. $content = $comment->comment_content; 
  5400. $title = substr($content, 8, (strpos($content, '</strong>') - 8)); 
  5401. $trackback_pings[] = array( 
  5402. 'pingTitle' => $title,  
  5403. 'pingURL' => $comment->comment_author_url,  
  5404. 'pingIP' => $comment->comment_author_IP 
  5405. ); 
  5406.  
  5407. return $trackback_pings; 
  5408.  
  5409. /** 
  5410. * Sets a post's publish status to 'publish'. 
  5411. * @since 1.5.0 
  5412. * @param array $args { 
  5413. * Method arguments. Note: arguments must be ordered as documented. 
  5414. * @type int $post_ID 
  5415. * @type string $username 
  5416. * @type string $password 
  5417. * } 
  5418. * @return int|IXR_Error 
  5419. */ 
  5420. public function mt_publishPost( $args ) { 
  5421. $this->escape( $args ); 
  5422.  
  5423. $post_ID = (int) $args[0]; 
  5424. $username = $args[1]; 
  5425. $password = $args[2]; 
  5426.  
  5427. if ( !$user = $this->login($username, $password) ) 
  5428. return $this->error; 
  5429.  
  5430. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5431. do_action( 'xmlrpc_call', 'mt.publishPost' ); 
  5432.  
  5433. $postdata = get_post($post_ID, ARRAY_A); 
  5434. if ( ! $postdata ) 
  5435. return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 
  5436.  
  5437. if ( !current_user_can('publish_posts') || !current_user_can('edit_post', $post_ID) ) 
  5438. return new IXR_Error(401, __('Sorry, you are not allowed to publish this post.')); 
  5439.  
  5440. $postdata['post_status'] = 'publish'; 
  5441.  
  5442. // retain old cats 
  5443. $cats = wp_get_post_categories($post_ID); 
  5444. $postdata['post_category'] = $cats; 
  5445. $this->escape($postdata); 
  5446.  
  5447. return wp_update_post( $postdata ); 
  5448.  
  5449. /** PingBack functions 
  5450. * specs on www.hixie.ch/specs/pingback/pingback 
  5451. */ 
  5452.  
  5453. /** 
  5454. * Retrieves a pingback and registers it. 
  5455. * @since 1.5.0 
  5456. * @param array $args { 
  5457. * Method arguments. Note: arguments must be ordered as documented. 
  5458. * @type string $pagelinkedfrom 
  5459. * @type string $pagelinkedto 
  5460. * } 
  5461. * @return string|IXR_Error 
  5462. */ 
  5463. public function pingback_ping( $args ) { 
  5464. global $wpdb; 
  5465.  
  5466. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5467. do_action( 'xmlrpc_call', 'pingback.ping' ); 
  5468.  
  5469. $this->escape( $args ); 
  5470.  
  5471. $pagelinkedfrom = str_replace( '&', '&', $args[0] ); 
  5472. $pagelinkedto = str_replace( '&', '&', $args[1] ); 
  5473. $pagelinkedto = str_replace( '&', '&', $pagelinkedto ); 
  5474.  
  5475. /** 
  5476. * Filters the pingback source URI. 
  5477. * @since 3.6.0 
  5478. * @param string $pagelinkedfrom URI of the page linked from. 
  5479. * @param string $pagelinkedto URI of the page linked to. 
  5480. */ 
  5481. $pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto ); 
  5482.  
  5483. if ( ! $pagelinkedfrom ) 
  5484. return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) ); 
  5485.  
  5486. // Check if the page linked to is in our site 
  5487. $pos1 = strpos($pagelinkedto, str_replace(array('http://www.', 'http://', 'https://www.', 'https://'), '', get_option('home'))); 
  5488. if ( !$pos1 ) 
  5489. return $this->pingback_error( 0, __( 'Is there no link to us?' ) ); 
  5490.  
  5491. // let's find which post is linked to 
  5492. // FIXME: does url_to_postid() cover all these cases already? 
  5493. // if so, then let's use it and drop the old code. 
  5494. $urltest = parse_url($pagelinkedto); 
  5495. if ( $post_ID = url_to_postid($pagelinkedto) ) { 
  5496. // $way 
  5497. } elseif ( isset( $urltest['path'] ) && preg_match('#p/[0-9]{1, }#', $urltest['path'], $match) ) { 
  5498. // the path defines the post_ID (archives/p/XXXX) 
  5499. $blah = explode('/', $match[0]); 
  5500. $post_ID = (int) $blah[1]; 
  5501. } elseif ( isset( $urltest['query'] ) && preg_match('#p=[0-9]{1, }#', $urltest['query'], $match) ) { 
  5502. // the querystring defines the post_ID (?p=XXXX) 
  5503. $blah = explode('=', $match[0]); 
  5504. $post_ID = (int) $blah[1]; 
  5505. } elseif ( isset($urltest['fragment']) ) { 
  5506. // an #anchor is there, it's either... 
  5507. if ( intval($urltest['fragment']) ) { 
  5508. // ...an integer #XXXX (simplest case) 
  5509. $post_ID = (int) $urltest['fragment']; 
  5510. } elseif ( preg_match('/post-[0-9]+/', $urltest['fragment']) ) { 
  5511. // ...a post id in the form 'post-###' 
  5512. $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); 
  5513. } elseif ( is_string($urltest['fragment']) ) { 
  5514. // ...or a string #title, a little more complicated 
  5515. $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); 
  5516. $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title ); 
  5517. if (! ($post_ID = $wpdb->get_var($sql)) ) { 
  5518. // returning unknown error '0' is better than die()ing 
  5519. return $this->pingback_error( 0, '' ); 
  5520. } else { 
  5521. // TODO: Attempt to extract a post ID from the given URL 
  5522. 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.' ) ); 
  5523. $post_ID = (int) $post_ID; 
  5524.  
  5525. $post = get_post($post_ID); 
  5526.  
  5527. if ( !$post ) // Post_ID not found 
  5528. 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.' ) ); 
  5529.  
  5530. if ( $post_ID == url_to_postid($pagelinkedfrom) ) 
  5531. return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) ); 
  5532.  
  5533. // Check if pings are on 
  5534. if ( !pings_open($post) ) 
  5535. 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.' ) ); 
  5536.  
  5537. // Let's check that the remote site didn't already pingback this entry 
  5538. if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) ) 
  5539. return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) ); 
  5540.  
  5541. // very stupid, but gives time to the 'from' server to publish ! 
  5542. sleep(1); 
  5543.  
  5544. $remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ); 
  5545.  
  5546. /** This filter is documented in wp-includes/class-http.php */ 
  5547. $user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) ); 
  5548.  
  5549. // Let's check the remote site 
  5550. $http_api_args = array( 
  5551. 'timeout' => 10,  
  5552. 'redirection' => 0,  
  5553. 'limit_response_size' => 153600, // 150 KB 
  5554. 'user-agent' => "$user_agent; verifying pingback from $remote_ip",  
  5555. 'headers' => array( 
  5556. 'X-Pingback-Forwarded-For' => $remote_ip,  
  5557. ),  
  5558. ); 
  5559.  
  5560. $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args ); 
  5561. $remote_source = $remote_source_original = wp_remote_retrieve_body( $request ); 
  5562.  
  5563. if ( ! $remote_source ) { 
  5564. return $this->pingback_error( 16, __( 'The source URL does not exist.' ) ); 
  5565.  
  5566. /** 
  5567. * Filters the pingback remote source. 
  5568. * @since 2.5.0 
  5569. * @param string $remote_source Response source for the page linked from. 
  5570. * @param string $pagelinkedto URL of the page linked to. 
  5571. */ 
  5572. $remote_source = apply_filters( 'pre_remote_source', $remote_source, $pagelinkedto ); 
  5573.  
  5574. // Work around bug in strip_tags(): 
  5575. $remote_source = str_replace( '<!DOC', '<DOC', $remote_source ); 
  5576. $remote_source = preg_replace( '/[\r\n\t ]+/', ' ', $remote_source ); // normalize spaces 
  5577. $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 ); 
  5578.   
  5579. preg_match( '|<title>([^<]*?)</title>|is', $remote_source, $matchtitle ); 
  5580. $title = isset( $matchtitle[1] ) ? $matchtitle[1] : ''; 
  5581. if ( empty( $title ) ) { 
  5582. return $this->pingback_error( 32, __( 'We cannot find a title on that page.' ) ); 
  5583.   
  5584. $remote_source = strip_tags( $remote_source, '<a>' ); // just keep the tag we need 
  5585.   
  5586. $p = explode( "\n\n", $remote_source ); 
  5587.   
  5588. $preg_target = preg_quote($pagelinkedto, '|'); 
  5589.   
  5590. foreach ( $p as $para ) { 
  5591. if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? 
  5592. preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context); 
  5593.   
  5594. // If the URL isn't in a link context, keep looking 
  5595. if ( empty($context) ) 
  5596. continue; 
  5597.   
  5598. // We're going to use this fake tag to mark the context in a bit 
  5599. // the marker is needed in case the link text appears more than once in the paragraph 
  5600. $excerpt = preg_replace('|\</?wpcontext\>|', '', $para); 
  5601.   
  5602. // prevent really long link text 
  5603. if ( strlen($context[1]) > 100 ) 
  5604. $context[1] = substr($context[1], 0, 100) . '…'; 
  5605.   
  5606. $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker 
  5607. $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker 
  5608. $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker 
  5609. $excerpt = trim($excerpt); 
  5610. $preg_marker = preg_quote($marker, '|'); 
  5611. $excerpt = preg_replace("|.*?\s(.{0, 100}$preg_marker.{0, 100})\s.*|s", '$1', $excerpt); 
  5612. $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper 
  5613. break; 
  5614.   
  5615. if ( empty($context) ) // Link to target not found 
  5616. 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.' ) ); 
  5617.   
  5618. $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 
  5619.   
  5620. $context = '[…] ' . esc_html( $excerpt ) . ' […]'; 
  5621. $pagelinkedfrom = $this->escape( $pagelinkedfrom ); 
  5622.   
  5623. $comment_post_ID = (int) $post_ID; 
  5624. $comment_author = $title; 
  5625. $comment_author_email = ''; 
  5626. $this->escape($comment_author); 
  5627. $comment_author_url = $pagelinkedfrom; 
  5628. $comment_content = $context; 
  5629. $this->escape($comment_content); 
  5630. $comment_type = 'pingback'; 
  5631.   
  5632. $commentdata = compact( 
  5633. 'comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email',  
  5634. 'comment_content', 'comment_type', 'remote_source', 'remote_source_original' 
  5635. ); 
  5636.   
  5637. $comment_ID = wp_new_comment($commentdata); 
  5638.   
  5639. /** 
  5640. * Fires after a post pingback has been sent. 
  5641. * @since 0.71 
  5642. * @param int $comment_ID Comment ID. 
  5643. */ 
  5644. do_action( 'pingback_post', $comment_ID ); 
  5645.  
  5646. /** translators: 1: URL of the page linked from, 2: URL of the page linked to */ 
  5647. return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $pagelinkedfrom, $pagelinkedto ); 
  5648.  
  5649. /** 
  5650. * Retrieve array of URLs that pingbacked the given URL. 
  5651. * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html 
  5652. * @since 1.5.0 
  5653. * @global wpdb $wpdb WordPress database abstraction object. 
  5654. * @param string $url 
  5655. * @return array|IXR_Error 
  5656. */ 
  5657. public function pingback_extensions_getPingbacks( $url ) { 
  5658. global $wpdb; 
  5659.  
  5660. /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ 
  5661. do_action( 'xmlrpc_call', 'pingback.extensions.getPingbacks' ); 
  5662.  
  5663. $url = $this->escape( $url ); 
  5664.  
  5665. $post_ID = url_to_postid($url); 
  5666. if ( !$post_ID ) { 
  5667. // We aren't sure that the resource is available and/or pingback enabled 
  5668. 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.' ) ); 
  5669.  
  5670. $actual_post = get_post($post_ID, ARRAY_A); 
  5671.  
  5672. if ( !$actual_post ) { 
  5673. // No such post = resource not found 
  5674. return $this->pingback_error( 32, __('The specified target URL does not exist.' ) ); 
  5675.  
  5676. $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) ); 
  5677.  
  5678. if ( !$comments ) 
  5679. return array(); 
  5680.  
  5681. $pingbacks = array(); 
  5682. foreach ( $comments as $comment ) { 
  5683. if ( 'pingback' == $comment->comment_type ) 
  5684. $pingbacks[] = $comment->comment_author_url; 
  5685.  
  5686. return $pingbacks; 
  5687.  
  5688. /** 
  5689. * Sends a pingback error based on the given error code and message. 
  5690. * @since 3.6.0 
  5691. * @param int $code Error code. 
  5692. * @param string $message Error message. 
  5693. * @return IXR_Error Error object. 
  5694. */ 
  5695. protected function pingback_error( $code, $message ) { 
  5696. /** 
  5697. * Filters the XML-RPC pingback error return. 
  5698. * @since 3.5.1 
  5699. * @param IXR_Error $error An IXR_Error object containing the error code and message. 
  5700. */ 
  5701. return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) );