Drupal 6: Silverpop XML API Module

Submitted by nigel on Tuesday 26th June 2012

This module integrates Drupal with the Silverpop Engaging Marketing Solutions XML API. So what does it do? On its own, actually very little. It is only of use to developers wishing to write code to interrogate the Silverpop back-end. This module provides the handshaking mechanism between a developer's code and Silverpop. There are currently two published and documented Silverpop APIs, viz SOAP API and XML API. This module has been built to use the XML API. There may well be detail differences between the two APIs so prospective developers should undertake the necessary research to ensure that the XML API delivers to their requirements.

The module is installed in the normal Drupal way. If you are unsure how to accomplish this, you probably don't have the prerequisite skills yet to use this module. Once it has been installed and enabled it will require configuration at admin/settings/silverpop. You will require login credentials (username and password) for most of the Silverpop API calls (but crucially not all). You will also require the url on the Silverpop system to where the API calls are directed. This is usually of the format api{n}.silverpop.com where {n} is a small integer, but you should check with your Silverpop contact. You should also configure the user permissions for Silverpop administration at admin/user/persmissions.

Using the Interface

The best way of learning how to use this interface is to check out the function silverpop_get_list_meta_data which retrieves meta data for a particular Silverpop database. It is included in the silverpop.module. Note: This is the only API call included in this module and can be used with your code for test purposes.

If you look at the silverpop_get_list_meta_data function you will note that the XML is constructed using the DOMDocument class. This would be the text book approach to building your own XML, but you could of course just assign strings. When you are constructing this XML, you should not include the Envelope + Body nodes since this module automatically does this for you.

This module returns an array back to the caller. The first element is a boolean which if TRUE says the call to Silverpop was successful. A NULL/FALSE value says there was a failure. The second array element is the DOMDocument object returned by Silverpop. You do not need to worry about parsing the XML and writing error logs with a returned error diagnostic / code - this module does it for you. The object is passed back whether there is an error or not in case the calling function needs the information provided in it.

Should you have any problems, your first port of call should be your Drupal website's error log. All failed calls are entered into the watchdog table and should give you a very good clue as to where the issues are.

Other Silverpop Modules

I have written a Silverpop AddRecipient Module which uses this module for interfacing with Silverpop.

silverpop.info
version = "6.x-1.00"
name = "Silverpop"
description = "Silverpop API Integration module"
core = 6.x
php = 5.2
package = "silverpop"
silverpop.module
<?php

// Copyright @badzillacouk http://www.badzilla.co.uk
// Licence GPL. This program may be distributed as per the terms of GPL and all credits
// must be retained
//
// If you find this script useful, please consider a donation to help me fund my web presence
// and encourage me to develop more products to be placed under the terms of GPL
// To donate, go to http://www.badzilla.co.uk and click on the donation button
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 





/**
 * Implementation of hook_menu().
 *
 */
function silverpop_menu() {

    
$items = array();
    
$items['admin/settings/silverpop'] = array(
        
'title' => 'Silverpop',
        
'description' => t('Silverpop API Settings'),
        
'page callback' => 'drupal_get_form',
        
'page arguments' => array('silverpop_admin_settings_form'),
        
'access arguments' => array('administer silverpop'),
        
'type' => MENU_NORMAL_ITEM,
    );

    return 
$items;
}



/**
 * Implementation of hook_perm().
 *
 */
function silverpop_perm() {
    
    return array(
'administer silverpop');
}



function 
silverpop_admin_settings_form() {

    
$form['silverpop_endpoint'] = array(
        
'#type' => 'textfield',
        
'#title' => t('Endpoint'),
        
'#description' => t('The url to the Silverpop API endpoint.'),
        
'#size' => 40,
        
'#maxlength' => 255,
        
'#required' => TRUE,
        
'#default_value' => variable_get('silverpop_endpoint'NULL),
    );

    
$form['silverpop_userid'] = array(
        
'#type' => 'textfield',
        
'#title' => t('Username'),
        
'#description' => t('Silverpop username.'),
        
'#size' => 40,
        
'#maxlength' => 255,
        
'#default_value' => variable_get('silverpop_userid'NULL),
    );

    
$form['silverpop_password'] = array(
        
'#type' => 'password',
        
'#title' => t('Password'),
        
'#description' => t('Silverpop password.'),
        
'#size' => 40,
        
'#maxlength' => 255,
        
'#default_value' => variable_get('silverpop_password'NULL),
    );

    return 
system_settings_form($form);
}




function 
silverpop_get_list_meta_data($listid) {

    
// Construct the XML for the Silverpop API call
    
$dom = new DOMDocument;
    
$list_id $dom->createTextNode($listid);
    
$list $dom->createElement('LIST_ID');

    
$method $dom->createElement('GetListMetaData');

    
$list->appendChild($list_id);
    
$method->appendChild($list);
    
$dom->appendChild($method);

    
module_load_include('inc''silverpop''silverpop_xml');
    
$get_list_meta_data = new silverpop_xml($domTRUE);
    return 
$get_list_meta_data->apicall();
}

?>
silverpop_xml.inc
<?php

// Copyright @badzillacouk http://www.badzilla.co.uk
// Licence GPL. This program may be distributed as per the terms of GPL and all credits
// must be retained
//
// If you find this script useful, please consider a donation to help me fund my web presence
// and encourage me to develop more products to be placed under the terms of GPL
// To donate, go to http://www.badzilla.co.uk and click on the donation button
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 



class silverpop_xml {

    private 
$wrapper;
    private 
$login_required;
    private 
$endpoint;
    private 
$userid;
    private 
$password;
    private 
$sessionid;


    
// Silverpop outgoing API call
    // Pass a @body DOMDocument without the Envelope/Body nodes
    // And a TRUE / FALSE depending upon whether a jsession is requred or not
    
public function __construct($body$login_required FALSE) {

        
// if we don't have a url, not much point in progressing
        
if (($this->endpoint variable_get('silverpop_endpoint'NULL)) == NULL)
            return array(
NULL);

        
// ditto credentials if required
        
if ($login_required) {
            if ((
$this->userid variable_get('silverpop_userid'NULL)) == NULL)
                return array(
NULL);  
            if ((
$this->password variable_get('silverpop_password'NULL)) == NULL)
                return array(
NULL);
        }

        
// Create the outgoing xml.
        
$this->wrapper $this->_build_envelope($body);
        
$this->login_required $login_required;
    }


    
// Top level API Call - all calling functions must go through this and not the lower-level function
    // Returns an array: First value FALSE or TRUE on success, and second value the DOMDocument response
    // (if converting the response to XML was possible)
    
public function apicall() {

        
$ret = array();

        if (
$this->login_required)
            if (!
$this->_login())
                return 
$ret;

        
$ret $this->_do_call();

        if (
$this->login_required)
            
$this->_logout();

        return 
$ret;
    }


    
// build the login call to get the session id
    
private function _login() {

        
$dom = new DOMDocument;
        
$user_text $dom->createTextNode($this->userid);
        
$user $dom->createElement('USERNAME');

        
$password_text $dom->createTextNode($this->password);
        
$password $dom->createElement('PASSWORD');

        
$method $dom->createElement('Login');

        
$user->appendChild($user_text);
        
$password->appendChild($password_text);
        
$method->appendChild($user);
        
$method->appendChild($password);
        
$dom->appendChild($method);

        list(
$retcode$response) = $this->_do_call($this->_build_envelope($dom));

        
// extract the sessionid if it is available
        
if (!$retcode
            return 
NULL;

        if (
is_object($response) and isset($response->getElementsByTagName('SESSIONID')->item(0)->nodeValue)) {
            
$this->sessionid $response->getElementsByTagName('SESSIONID')->item(0)->nodeValue;
            return 
TRUE;
        }

        return 
NULL;
    }


    
// build the logout call
    
private function _logout() {

        
$dom = new DOMDocument;
        
$method $dom->createElement('Logout');
        
$dom->appendChild($method);

        
$this->_do_call($this->_build_envelope($dom));

        return 
TRUE;
    }


    
// build the xml envelope
    // Pass the inner method and paramters as a DOMDocument
    // Return the stringified xml
    
private function _build_envelope($inner) {

        
// Create the outgoing xml.
        // First create the outer wrapper
        
$wrapper = new DOMDocument;
        
$envelope $wrapper->createElement('Envelope');
        
$body_tag $wrapper->createElement('Body');

        
$wrapper->appendChild($envelope);
        
$envelope->appendChild($body_tag);

        
// Now insert the passed parameters into this wrapper
        
$node $inner->documentElement;
        
$node $wrapper->importNode($nodetrue);
        
$wrapper->getElementsByTagName('Body')->item(0)->appendChild($node);

        return 
$wrapper->saveXML();
    }


    
// low level API call not available to clients
    
private function _do_call($wrapper NULL) {
    
        
// ok now ready to go
        
if (!$wrapper)
            
$data "xml=" urlencode($this->wrapper);
        else
            
$data "xml=" urlencode($wrapper);

        
// open connection
        // port 80, timeout of 20
        
if (($sock = @fsockopen($this->endpoint80$errno$errstr20)) == NULL) {
            
watchdog('silverpop''Could not connect to host with error number: @errno and diagnostic: @errstr'
                    array(
'@errno' => $errno'@errstr' => $errstr), WATCHDOG_ERROR);
            return array();
        }

        
// outgoing message
        
$jsessionid '';
        if (
$this->login_required)
            
$jsessionid ';jsessionid=' $this->sessionid;
        
fputs($sock"POST /servlet/XMLAPI{$jsessionid} HTTP/1.1\n");
        
fputs($sock"Host: " $this->endpoint "\n");
        
fputs($sock"Content-type: application/x-www-form-urlencoded\n");
        
fputs($sock"Content-length: " strlen ($data) . "\n");
        
fputs($sock"Connection: close\n\n");
        
fputs($sock$data);

        
// incoming
        
$buffer "";
        while (!
feof($sock))
            
$buffer .= fgets($sock);

        
fclose($sock);

        
// Ok so was there an error? If so, stick it in the log
        
if (($pos stripos($buffer'<Envelope>')) === NULL) {
            
watchdog('silverpop''Unspecified error diagnostic. Output dump: @dump', array('@dump' => $buffer), WATCHDOG_ERROR);
            return array(
NULL);
        } else {
            
$epos stripos($buffer'</Envelope>') + strlen('</Envelope>');
            
$b_body substr($buffer$pos$epos $pos);

            
$response = new DOMDocument;
            
$response->loadXML($b_body);

            
$success $response->getElementsByTagName('SUCCESS');
            
$fault $response->getElementsByTagName('FaultString');
            
$errorid $response->getElementsByTagName('errorid');

            if (
$success->length and strtoupper($response->getElementsByTagName('SUCCESS')->item(0)->nodeValue) == 'FALSE') {
                
$err_arr = array();
                
$msg 'API call failed.';

                
// did we get an error message?
                
if ($fault->length) { 
                    
$msg .= ' Diagnostic: @fault'
                    
$err_arr['@fault'] = $response->getElementsByTagName('FaultString')->item(0)->nodeValue
                }

                
// did we get an error number?
                
if ($errorid->length) {
                    
$msg .= ' Error code: @errorid';
                    
$err_arr['@errorid'] = $response->getElementsByTagName('errorid')->item(0)->nodeValue
                }
                    
                
watchdog('silverpop'$msg$err_arrWATCHDOG_ERROR);
                return array(
FALSE$response);
            }
        }

        return array(
TRUE$response);  
    }



}

?>
blog terms
Drupal Drupal 6