Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm working with a custom XML-RPC method to capture some information for a plugin I am building. When activated the user has to authenticate themselves by entering there user/password given to them when purchasing the plugin on my WP site.

The plugin uses this code:

if ( isset( $_POST['username'] ) && isset( $_POST['password'] ) ) {

        /* check against remote server */
        require_once( ABSPATH . WPINC . '/class-IXR.php' );
        $this->client = new IXR_Client( trailingslashit( CUSTOMLOGIN_UPDATE_API ) . 'xmlrpc.php' ); //*/

        $url = ( ( is_multisite() ) ? network_site_url() : site_url() );

        $client_request_args = array(
            'username'  => $_POST['username'],
            'password'  => $_POST['password'],
            'plugin'    => CUSTOMLOGINPRO_BASENAME,
            'url'       => $url
        );

        if ( !$this->client->query( 'thefrosty.is_user_authorized', $client_request_args ) ) {
            add_action( 'admin_notices', array( $this, 'error_notice' ) );
            return false;
        }

        $this->settings = get_option( CUSTOMLOGINPRO . '_settings', array() );
        $this->settings['api-key'] = $this->client->getResponse();
        update_option( CUSTOMLOGINPRO . '_settings', $this->settings );
        header( 'Location: ' . admin_url( 'options-general.php?page=' . CUSTOMLOGINPRO ) );
        die();

    } 

The form submits and looks like its kinda working, getting a 200 error when working locally, but tested with another plugin that send same error code, but is sending responses from host (no worries yet).

What I am stuck on is the back end code. I've created a class in a core plugin on my WP install. I am assuming this is where it needs to be. I am missing some fault code and error message responses, not sure how to do those. Just looking for a little push in the right direction..

As you can see I am also testing the user against a WooCommerce order (code from WC, should be good) but then trying to update the order meta meta might need to fixes to save the users URLs.

class frosty_core {

function __construct() {
    add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
}

/**
 * Create our custom XML-rpc method.
 * @ref http://kovshenin.com/2010/04/custom-xml-rpc-methods-in-wordpress-2151/
 */
function xmlrpc_methods( $methods ) {
    $methods['thefrosty.is_user_authorized'] = array( $this, 'thefrosty_plugin_callback' );
    return $methods;
}

/**
 * XML-prc mothod
 */
function thefrosty_plugin_callback( $args ) {

    // Parse the arguments, assuming they're in the correct order
    $username       = $args[0];
    $password       = $args[1];
    $plugin_name    = $args[2];
    $url            = $args[3];

    global $wp_xmlrpc_server;

    // Let's run a check to see if credentials are okay
    if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
        return $wp_xmlrpc_server->error;
    }

    if ( !class_exists( 'woocommerce' ) ) return 'error, please try again later';

    /* Get the user ID by name */
    $current_user_id = get_userdatabylogin( $username );

    /* woocommerce/shortcodes/shortcode-my_account.php */
    $args = array(
        'numberposts'     => -1,
        'meta_key'        => '_customer_user',
        'meta_value'      => $current_user_id,
        'post_type'       => 'shop_order',
        'post_status'     => 'publish' 
    );
    $customer_orders = get_posts( $args );      
    $match = false;

    foreach ( $customer_orders as $customer_order ) :
        $order = &new woocommerce_order();
        $order->populate( $customer_order );

        $status = get_term_by( 'slug', $order->status, 'shop_order_status' );
        if ( 'completed' !== $status->name ) return; //error, order not completed //*/

        if ( $plugin_name !== $order->items->name ) return; // you have not purchased this plugin //*/

        $match  = true;
        $apikey = $order->order_key;
    endforeach;

    if ( isset( $match ) && $match ) {
        /* woocommerce/admin/writepanels/writepanel-order_data.php */
        add_filter( 'update_order_item', create_function( '$order_items', '
            $new_meta   = &new order_item_meta();
            $meta_name  = "active_urls";
            $meta_value = esc_url( $url );

            $new_meta->add( $meta_name, $meta_value );
            return $order_items["item_meta"] => $new_meta->meta;' ) );

        return $apikey;
    }
    else return false;
}

}

share|improve this question

2 Answers 2

I sent it to you on Twitter, but here it is again.

I made this little class to help me do XML-RPC faster.

abstract class MZAXMLRPC {
    protected $calls = Array();
    protected $namespace = "myxmlrpc";

    function __construct($namespace){
        $this->namespace = $namespace;
        $reflector = new ReflectionClass($this);
        foreach ( $reflector->getMethods(ReflectionMethod::IS_PUBLIC) as $method){
            if ($method->isUserDefined() && $method->getDeclaringClass()->name != get_class()){
                $this->calls[] = $method->name;
            }
        }
        add_filter('xmlrpc_methods', array($this, 'xmlrpc_methods'));
    }

    public function xmlrpc_methods($methods)
    {
        foreach ($this->calls as $call){
            $methods[$this->namespace . "." . $call] = array($this, "dispatch");
        }
        return $methods;
    }

    public function dispatch($args){
        global $wp_xmlrpc_server;

        $username   = $args[1];
        $password   = $args[2];
        $data = $args[3];

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

        $call = $this->get_called_method();

        if (method_exists($this, $call)){
            $status = call_user_func_array(array($this, $call), array($data));
            return $status;
        }else{
            return "Method not allowed";
        }

    }

    private function get_called_method(){
        global $wp_xmlrpc_server;
        $call = $wp_xmlrpc_server->message->methodName;
        $pieces = explode(".", $call);
        return $pieces[1];
    }

}

This is an abstract class. You won't instantiate objects out of it. What you'll do is create a new class that inherits from MZAXMLRPC, and in it you'll make public methods for each XML-RPC call you want to expose.

The constructor uses Reflection to find all the user-defined public methods of the child class that is inheriting from it. Then it will tell WordPress that we're accepting XML-RPC calls for this methods. The XML-RPC methods are exposed with the name $namespace.$public_method_name.

All those new XML-RPC will arrive to the same method, dispatch. This method will fist validate the user/pass for the remote call, then it will check that effectively there is a method declared to take care of the XML-RPC call. If all validates ok it will dispatch the call to the appropriate method of the child class, passing along all the data that came through the XML-RPC server.

Enough gibberish already! The only thing you need to do is:

class MY_XMLRPC extends MZAXMLRPC{

    public function QuoteUpload($data){

        if (!isset($data["author"]))
            return "Missing 'author' parameter";

        if (!isset($data["quote"]))
            return "Missing 'quote' parameter";

        $author = $data["author"];
        $quote = $data["quote"];

        $new_post = array(
            'post_status' => 'publish',
            'post_type' => 'mzaquotes',
            'post_title' => $quote
        );

        $new_post_id = wp_insert_post($new_post);
        wp_set_post_terms( $new_post_id, $author, 'quote_author' );

        return "OK";
    }
}

new MY_XMLRPC('my_function');

This little piece of code will expose a XML-RPC method called my_function.QuoteUpload. The parent class will deal with authentication and WordPress APIs for you.

share|improve this answer
    
If it's not working you can return serialize($wp_xmlrpc_server) inside dispatch() to see what the server is returning. –  MZAweb Jan 19 '12 at 20:30

Nothing looks wrong with your code. You're definitely adding the XMLRPC endpoint correctly. The only issue I see is with this code:

 if ( isset( $match ) && $match ) {
    /* woocommerce/admin/writepanels/writepanel-order_data.php */
    add_filter( 'update_order_item', create_function( '$order_items', '
        $new_meta   = &new order_item_meta();
        $meta_name  = "active_urls";
        $meta_value = esc_url( $url );

        $new_meta->add( $meta_name, $meta_value );
        return $order_items["item_meta"] => $new_meta->meta;' ) );

    return $apikey;
}

It might be that I don't know WooCommerce well enough ... but you should be invoking some kind of function here, not adding a filter. Your code will only be applied if the filter is hit ... and in a typical XMLRPC request, it won't be.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.