: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( isset( $content_struct['custom_fields'] ) ) {
$this->set_custom_fields( $post_id, $content_struct['custom_fields'] );
if ( isset( $content_struct['wp_post_thumbnail'] ) ) {
// Empty value deletes, non-empty value adds/updates.
if ( empty( $content_struct['wp_post_thumbnail'] ) ) {
delete_post_thumbnail( $post_id );
if ( set_post_thumbnail( $post_id, $content_struct['wp_post_thumbnail'] ) === false ) {
return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
unset( $content_struct['wp_post_thumbnail'] );
$thisEnclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null;
$this->add_enclosure_if_new( $post_id, $thisEnclosure );
$this->attach_uploads( $ID, $post_content );
// Handle post formats if assigned, validation is handled earlier in this function.
if ( isset( $content_struct['wp_post_format'] ) ) {
set_post_format( $post_id, $content_struct['wp_post_format'] );
* Fires after a post has been successfully updated via the XML-RPC MovableType API.
* @param int $post_id ID of the updated post.
* @param array $args An array of arguments to update the post.
do_action( 'xmlrpc_call_success_mw_editPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
* Method arguments. Note: arguments must be ordered as documented.
* @type string $1 Username.
* @type string $2 Password.
* @return array|IXR_Error
public function mw_getPost( $args ) {
$post_id = (int) $args[0];
$user = $this->login( $username, $password );
$postdata = get_post( $post_id, ARRAY_A );
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'metaWeblog.getPost', $args, $this );
if ( '' !== $postdata['post_date'] ) {
$post_date = $this->_convert_date( $postdata['post_date'] );
$post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] );
$post_modified = $this->_convert_date( $postdata['post_modified'] );
$post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] );
$catids = wp_get_post_categories( $post_id );
foreach ( $catids as $catid ) {
$categories[] = get_cat_name( $catid );
$tags = wp_get_post_tags( $post_id );
if ( ! empty( $tags ) ) {
foreach ( $tags as $tag ) {
$tagnames[] = $tag->name;
$tagnames = implode( ', ', $tagnames );
$post = get_extended( $postdata['post_content'] );
$link = get_permalink( $postdata['ID'] );
$author = get_userdata( $postdata['post_author'] );
$allow_comments = ( 'open' === $postdata['comment_status'] ) ? 1 : 0;
$allow_pings = ( 'open' === $postdata['ping_status'] ) ? 1 : 0;
// Consider future posts as published.
if ( 'future' === $postdata['post_status'] ) {
$postdata['post_status'] = 'publish';
$post_format = get_post_format( $post_id );
if ( empty( $post_format ) ) {
$post_format = 'standard';
if ( is_sticky( $post_id ) ) {
foreach ( (array) get_post_custom( $post_id ) as $key => $val ) {
if ( 'enclosure' === $key ) {
foreach ( (array) $val as $enc ) {
$encdata = explode( "\n", $enc );
$enclosure['url'] = trim( htmlspecialchars( $encdata[0] ) );
$enclosure['length'] = (int) trim( $encdata[1] );
$enclosure['type'] = trim( $encdata[2] );
'dateCreated' => $post_date,
'userid' => $postdata['post_author'],
'postid' => $postdata['ID'],
'description' => $post['main'],
'title' => $postdata['post_title'],
// Commented out because no other tool seems to use this.
// 'content' => $entry['post_content'],
'categories' => $categories,
'mt_excerpt' => $postdata['post_excerpt'],
'mt_text_more' => $post['extended'],
'wp_more_text' => $post['more_text'],
'mt_allow_comments' => $allow_comments,
'mt_allow_pings' => $allow_pings,
'mt_keywords' => $tagnames,
'wp_slug' => $postdata['post_name'],
'wp_password' => $postdata['post_password'],
'wp_author_id' => (string) $author->ID,
'wp_author_display_name' => $author->display_name,
'date_created_gmt' => $post_date_gmt,
'post_status' => $postdata['post_status'],
'custom_fields' => $this->get_custom_fields( $post_id ),
'wp_post_format' => $post_format,
'date_modified' => $post_modified,
'date_modified_gmt' => $post_modified_gmt,
if ( ! empty( $enclosure ) ) {
$resp['enclosure'] = $enclosure;
$resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] );
return new IXR_Error( 404, __( 'Sorry, no such post.' ) );
* Retrieves list of recent posts.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type int $3 Optional. Number of posts.
* @return array|IXR_Error
public function mw_getRecentPosts( $args ) {
if ( isset( $args[3] ) ) {
$query = array( 'numberposts' => absint( $args[3] ) );
$user = $this->login( $username, $password );
if ( ! current_user_can( 'edit_posts' ) ) {
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts', $args, $this );
$posts_list = wp_get_recent_posts( $query );
foreach ( $posts_list as $entry ) {
if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) {
$post_date = $this->_convert_date( $entry['post_date'] );
$post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
$post_modified = $this->_convert_date( $entry['post_modified'] );
$post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] );
$catids = wp_get_post_categories( $entry['ID'] );
foreach ( $catids as $catid ) {
$categories[] = get_cat_name( $catid );
$tags = wp_get_post_tags( $entry['ID'] );
if ( ! empty( $tags ) ) {
foreach ( $tags as $tag ) {
$tagnames[] = $tag->name;
$tagnames = implode( ', ', $tagnames );
$post = get_extended( $entry['post_content'] );
$link = get_permalink( $entry['ID'] );
// Get the post author info.
$author = get_userdata( $entry['post_author'] );
$allow_comments = ( 'open' === $entry['comment_status'] ) ? 1 : 0;
$allow_pings = ( 'open' === $entry['ping_status'] ) ? 1 : 0;
// Consider future posts as published.
if ( 'future' === $entry['post_status'] ) {
$entry['post_status'] = 'publish';
$post_format = get_post_format( $entry['ID'] );
if ( empty( $post_format ) ) {
$post_format = 'standard';
'dateCreated' => $post_date,
'userid' => $entry['post_author'],
'postid' => (string) $entry['ID'],
'description' => $post['main'],
'title' => $entry['post_title'],
// Commented out because no other tool seems to use this.
// 'content' => $entry['post_content'],
'categories' => $categories,
'mt_excerpt' => $entry['post_excerpt'],
'mt_text_more' => $post['extended'],
'wp_more_text' => $post['more_text'],
'mt_allow_comments' => $allow_comments,
'mt_allow_pings' => $allow_pings,
'mt_keywords' => $tagnames,
'wp_slug' => $entry['post_name'],
'wp_password' => $entry['post_password'],
'wp_author_id' => (string) $author->ID,
'wp_author_display_name' => $author->display_name,
'date_created_gmt' => $post_date_gmt,
'post_status' => $entry['post_status'],
'custom_fields' => $this->get_custom_fields( $entry['ID'] ),
'wp_post_format' => $post_format,
'date_modified' => $post_modified,
'date_modified_gmt' => $post_modified_gmt,
'sticky' => ( 'post' === $entry['post_type'] && is_sticky( $entry['ID'] ) ),
'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ),
* Retrieves the list of categories on a given blog.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @return array|IXR_Error
public function mw_getCategories( $args ) {
$user = $this->login( $username, $password );
if ( ! current_user_can( 'edit_posts' ) ) {
return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'metaWeblog.getCategories', $args, $this );
$categories_struct = array();
$cats = get_categories( array( 'get' => 'all' ) );
foreach ( $cats as $cat ) {
$struct['categoryId'] = $cat->term_id;
$struct['parentId'] = $cat->parent;
$struct['description'] = $cat->name;
$struct['categoryDescription'] = $cat->description;
$struct['categoryName'] = $cat->name;
$struct['htmlUrl'] = esc_html( get_category_link( $cat->term_id ) );
$struct['rssUrl'] = esc_html( get_category_feed_link( $cat->term_id, 'rss2' ) );
$categories_struct[] = $struct;
return $categories_struct;
* Uploads a file, following your settings.
* Adapted from a patch by Johann Richard.
* @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @return array|IXR_Error
public function mw_newMediaObject( $args ) {
$username = $this->escape( $args[1] );
$password = $this->escape( $args[2] );
$name = sanitize_file_name( $data['name'] );
$user = $this->login( $username, $password );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject', $args, $this );
if ( ! current_user_can( 'upload_files' ) ) {
$this->error = new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) );
if ( is_multisite() && upload_is_user_over_quota( false ) ) {
$this->error = new IXR_Error(
/* translators: %s: Allowed space allocation. */
__( 'Sorry, you have used your space allocation of %s. Please delete some files to upload more files.' ),
size_format( get_space_allowed() * MB_IN_BYTES )
* Filters whether to preempt the XML-RPC media upload.
* Returning a truthy value will effectively short-circuit the media upload,
* returning that value as a 500 error instead.
* @param bool $error Whether to pre-empt the media upload. Default false.
$upload_err = apply_filters( 'pre_upload_error', false );
return new IXR_Error( 500, $upload_err );
$upload = wp_upload_bits( $name, null, $bits );
if ( ! empty( $upload['error'] ) ) {
/* translators: 1: File name, 2: Error message. */
$errorString = sprintf( __( 'Could not write file %1$s (%2$s).' ), $name, $upload['error'] );
return new IXR_Error( 500, $errorString );
// Construct the attachment array.
if ( ! empty( $data['post_id'] ) ) {
$post_id = (int) $data['post_id'];
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
'post_type' => 'attachment',
'post_parent' => $post_id,
'post_mime_type' => $type,
'guid' => $upload['url'],
$id = wp_insert_attachment( $attachment, $upload['file'], $post_id );
wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
* Fires after a new attachment has been added via the XML-RPC MovableType API.
* @param int $id ID of the new attachment.
* @param array $args An array of arguments to add the attachment.
do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
$struct = $this->_prepare_media_item( get_post( $id ) );
$struct['id'] = $struct['attachment_id'];
$struct['file'] = $struct['title'];
$struct['url'] = $struct['link'];
* MovableType API functions.
* Specs archive on http://web.archive.org/web/20050220091302/http://www.movabletype.org:80/docs/mtmanual_programmatic.html
* Retrieves the post titles of recent posts.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type int $3 Optional. Number of posts.
* @return array|IXR_Error