裏紙に書く程度の内容

wordpress xml rpcのuploadFileで画像を上書きする

wordpressのxml-rpcのwp.uploadFileを使用する際に、同名のファイルがある場合は上書きする方法についてです。

以前のエントリでver.4.3.1 の場合について書きましたが、4.4になると上書きのパラメータである、”overwrite”自体がなくなっているため、対処できません。

今回はxml-rpcに処理メソッドを追加して上書きを行います。

処理ルートの確認

xmlrpc.php にアクセスすると、以下の部分で処理が呼び出されているようです。

/**
 * Filter the class used for handling XML-RPC requests.
 *
 * @since 3.1.0
 *
 * @param string $class The name of the XML-RPC server class.
 */
$wp_xmlrpc_server_class = apply_filters( 'wp_xmlrpc_server_class', 'wp_xmlrpc_server' );
$wp_xmlrpc_server = new $wp_xmlrpc_server_class;

// Fire off the request
$wp_xmlrpc_server->serve_request();

exit;

呼び出されるwp_xmlrpc_server_classserve_request() は以下のようになっています。

    /**
     * @access public
     */
    public function serve_request() {
        $this->IXR_Server($this->methods);
    }

また、$this->methodsはコンストラクタで

        $this->methods = array(
            // WordPress API
            'wp.getUsersBlogs'      => 'this:wp_getUsersBlogs',
            'wp.newPost'            => 'this:wp_newPost',
            'wp.editPost'           => 'this:wp_editPost',
        ( 以下略 )

のように定義されています。 どうやらここで、実行するメソッドの定義をしているようです。

というか、コメントに

    /**
     * Register all of the XMLRPC methods that XMLRPC server understands.
     *
     * Sets up server and method property. Passes XMLRPC
     * methods through the 'xmlrpc_methods' filter to allow plugins to extend
     * or replace XMLRPC methods.
     *
     * @since 1.5.0
     */

って書いてあります。フィルタを使ってここに処理する関数を追加すればよさげです。

フィルタを設定する

では実際に追加してみます。

function.php に下記コードを追加します。

function appendXmlRpcMethods ($methods)
{
    $methods['my.uploadFile'] = 'myUploadFile';
    return $methods;

}
add_filter('xmlrpc_methods', 'appendXmlRpcMethods');

function myUploadFile($args)
{

}

ここではmy.uploadFileという処理種別(?)を追加しました。 クライアント側から’wp.uploadFile’の代わりに’my.uploadFile’を指定すれば、自作の’myUploadFile()’が実行されるようになります。

アップロード処理の実装

続いて処理内容を実装していきます。 先程追加したmyUploadFile()に任意の処理を書いていけばOKです。

今回やりたいのはwp.uploadFile処理の変更なので、その処理を行っている_mwnewMediaObject()(wp-includes/class-wp-xmlrpc-server.php にあります)の中身をまるっとコピーして修正することにします。

できたのはこちら。

function myUploadFile($args)
{
    global $wpdb;
    global $wp_xmlrpc_server;   // (1) 追加

    $username = $wp_xmlrpc_server->escape( $args[1] );
    $password = $wp_xmlrpc_server->escape( $args[2]);
    $data     = $args[3];

    $name = sanitize_file_name( $data['name'] );
    $type = $data['type'];
    $bits = $data['bits'];

    if ( !$user = $wp_xmlrpc_server->login($username, $password) )
        return $wp_xmlrpc_server->error;

    /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
    do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject' );

    if ( !current_user_can('upload_files') ) {
        $wp_xmlrpc_server->error = new IXR_Error( 401, __( 'You do not have permission to upload files.' ) );
        return $wp_xmlrpc_server->error;
    }

    if ( is_multisite() && upload_is_user_over_quota( false ) ) {
        $wp_xmlrpc_server->or = new IXR_Error( 401, __( 'Sorry, you have used your space allocation.' ) );
        return $wp_xmlrpc_server->error;
    }

    /**
     * Filter whether to preempt the XML-RPC media upload.
     *
     * Passing a truthy value will effectively short-circuit the media upload,
     * returning that value as a 500 error instead.
     *
     * @since 2.1.0
     *
     * @param bool $error Whether to pre-empt the media upload. Default false.
     */
    if ( $upload_err = apply_filters( 'pre_upload_error', false ) ) {
        return new IXR_Error( 500, $upload_err );
    }

    // (2) add -----------
    // ファイル格納前に、同名ファイルがある場合は一旦削除
    if ( !empty($data['overwrite']) && ($data['overwrite'] == true) ) {
        $old_file = $wpdb->get_row("select ID, guid from {$wpdb->posts} where post_title = '{$name}' and post_type = 'attachment'");

        $old_file_path = '.'. parse_url($old_file->guid, PHP_URL_PATH);
        if (file_exists($old_file_path) && is_file($old_file_path)) {
            unlink($old_file_path);
        }
    }
    // add -----------

    $upload = wp_upload_bits($name, null, $bits);

    if ( ! empty($upload['error']) ) {
        $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
        return new IXR_Error(500, $errorString);
    }

    // Construct the attachment array
    $post_id = 0;
    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 cannot edit this post.' ) );
    }
    $attachment = array(
        'post_title' =>ame,
        'post_content' => '',
        'post_type' => 'attachment',
        'post_parent' => $post_id,
        'post_mime_type' => $type,
        'guid' => $upload[ 'url' ]
    );

    // Save the data
    // (3) change -------------
    //$id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );

    if ( !empty($data['overwrite']) && ($data['overwrite'] == true) ) {

        if (is_null($old_file)) {

            // 同名ファイルがなければinsert
            $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
        } else {

            // 同名ファイルがある場合はinsertしない
            $id = $old_file->ID;
        }
    }
    // change -------------

    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.
     *
     * @since 3.4.0
     *
     * @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 );

    // (4) change
    //$struct = $wp_xmlrpc_server->_prepare_media_item( get_post( $id ) );
    $struct = myPrepareMediaItem(get_post($id));

    // Deprecated values
    $struct['id']   = $struct['attachment_id'];
    $struct['file'] = $struct['title'];
    $struct['url']  = $struct['link'];

    return $struct;

}

// (5) 追加
function myPrepareMediaItem( $media_item, $thumbnail_size = 'thumbnail' )
{
    global $wp_xmlrpc_server;   // 追加
    $_media_item = array(
        'attachment_id'    => strval( $media_item->ID ),
        'date_created_gmt' => $wp_xmlrpc_server->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
        'parent'           => $media_item->post_parent,
        'link'             => wp_get_attachment_url( $media_item->ID ),
        'title'            => $media_item->post_title,
        'caption'          => $media_item->post_excerpt,
        'description'      => $media_item->post_content,
        'metadata'         =>_get_attachment_metadata( $media_item->ID ),
        'type'             => $media_item->post_mime_type
    );

    $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
    if ( $thumbnail_src )
        $_media_item['thumbnail'] = $thumbnail_src[0];
    else
        $_media_item['thumbnail'] = $_media_item['link'];

    // change
    // return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
    return $_media_item;
}

なにやら長ったらしいですが修正ポイントは少ないのでご心配なく。

$thisの書き換え

元の_mwnewMediaObject()では$this->を使っているところがあるため、そのままでは動きません。

(1)の部分で_wp_xmlrpcserverのインスタンスを呼び出して、関数内の$thisを全て$wp_xmlrpc_serverに書き換えておきます。

同名ファイルの削除

続いて(2) // add ----------- で囲われた部分。ここでoverwriteが指定された場合、同名の既存ファイルは一旦削除します。

直後に続く、$upload = wp_upload_bits($name, null, $bits);で実際にファイルが書き込まれるので、削除処理はこの行より前で行っておく必要があります。

wp_postへの登録部分

さらに(3)、// change ------------- の部分。

overwriteの指定、既存ファイルのデータがdbにあるかどうかを判断し、ない場合(=新規)の場合は元々あった$id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );を実行、

そうでない場合は_wp_insertattachment()は実行せずに後に続く処理のために既存データのIDのみを渡しておきます。

protectedメソッド呼び出し部

最後に(4)及び(5)。

__prepare_mediaitem()protectedメソッドのためここでは呼び出せません。代わりに同等の関数を作ってそれを呼び出します。

中身は _wp_xmlrpc_serverclassの__prepare_mediaitem()をコピーしてきて使います。これも$this$wp_xmlrpc_serverに変更しておきます。

また、フィルタは要らないのでreturnの部分を変更しています。

これでwp.uploadFileの代わりにmy.uploadFileを使えば消えてしまったoverwriteパラメータが使えます。

残された問題点

とりあえず修正しましたが、上記コードにはいくつか問題があります。

まず、overwrite指定されたかどうかの判別をしているif ( !empty($data['overwrite']) && ($data['overwrite'] == true) )。 これを2か所でやっているのは何とも気持ち悪い。

それから事前に既存ファイルを削除してしまっているところ。

_wp_uploadbits()より前で削除が必要だったのですが、_wp_uploadbits()の後でエラーチェックされ、処理を抜けることがあります。

このとき、元のファイルは事前に削除してしまっているのでときすでにおすし。

きちんとする処理する場合は、このへんを修正しなければなりませんのでご注意ください。


今回はuploadFileの拡張みたいな感じでしたが、同じような方法でxml-rpcのapiを追加する場合のご参考になれば。

URABLO
広告
Index
広告