You are here

CMBase.php in Campaign Monitor 6.2

Same filename and directory in other branches
  1. 5.2 lib/CMBase.php
  2. 6.3 lib/CMBase.php

File

lib/CMBase.php
View source
<?php

/**
* LICENSE
* -------------------
* Copyright (c) 2007-2009, Kaiser Shahid <knitcore@yahoo.com> and
* Campaign Monitor <support@campaignmonitor.com>
* All rights reserved.
*
* This software is licensed under the BSD License:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Kaiser Shahid or Campaign Monitor nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Kaiser Shahid "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Kaiser Shahid BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @package CampaignMonitorLib
*/

/**
* This is an all-inclusive package for interfacing with Campaign Monitor's services. It
* supports SOAP, GET, and POST seamlessly (just set the $method property to 'soap', 'get', 
* or 'post' before making a call) and always returns the same view of data regardless of
* the method used to call the service.
*
* See README for more information on usage and details.
*
* CHANGES: 2008-04-28
* -------------------
* - Now compatible with PHP4. Biggest changes include removing reliance on
*   enhanced OOP syntax, and using XML Parser functions instead of SimpleXML.
* - Base class (CMBase) branches into CampaignMonitor and MailBuild
* - CMBase contains all the shared API calls (and extended functionality 
*   related to those) between both classes.
*
* @package CampaignMonitorLib
* @subpackage CMBase
* @version 1.4.3
* @author Kaiser Shahid <knitcore@yahoo.com> (www.qaiser.net)
* @copyright 2007-2009
* @see http://www.campaignmonitor.com/api/
*/
define('PHPVER', phpversion());

// WARNING: this is needed to keep the socket from apparently hanging (even when it should be done reading)
// NOTE: using a timeout (SOCKET_TIMEOUT) that's passed when calling fsockopen. safer thing to do.

//ini_set( 'default_socket_timeout', 1 );
define('SOCKET_TIMEOUT', 1);
class CMBase {
  var $api = '', $campaign_id = 0, $client_id = 0, $list_id = 0;
  var $method = 'get', $url = '', $soapAction = '', $curl = true, $curlExists = true;

  // debugging options
  var $debug_level = 0, $debug_request = '', $debug_response = '', $debug_url = '', $debug_info = array(), $show_response_headers = 0;

  /**
   * @param string $api Your API key.
   * @param string $client The default ClientId you're going to work with.
   * @param string $campaign The default CampaignId you're going to work with.
   * @param string $list The default ListId you're going to work with.
   * @param string $method Determines request type. Values are either get, post, or soap.
   */
  function CMBase($api = null, $client = null, $campaign = null, $list = null, $method = 'get') {
    $this->api = $api;
    $this->client_id = $client;
    $this->campaign_id = $campaign;
    $this->list_id = $list;
    $this->method = $method;
    $this->curlExists = function_exists('curl_init') && function_exists('curl_setopt');
  }

  /**
   * The direct way to make an API call. This allows developers to include new API
   * methods that might not yet have a wrapper method as part of the package.
   *
   * @param string $action The API call.
   * @param array $options An associative array of values to send as part of the request.
   * @return array The parsed XML of the request.
   */
  function makeCall($action = '', $options = array()) {

    // NEW [2008-06-24]: switch to soap automatically for these calls
    $old_method = $this->method;
    if ($action == 'Subscriber.AddWithCustomFields' || $action == 'Subscriber.AddAndResubscribeWithCustomFields' || $action == 'Campaign.Create') {
      $this->method = 'soap';
    }
    if (!$action) {
      return null;
    }
    $url = $this->url;

    // DONE: like facebook's client, allow for get/post through the file wrappers
    // if curl isn't available. (or maybe have curl-emulating functions defined
    // at the bottom of this script.)

    //$ch = curl_init();
    if (!isset($options['header'])) {
      $options['header'] = array();
    }
    $options['header'][] = 'User-Agent: CMBase URL Handler 1.5';
    $postdata = '';
    $method = 'GET';
    if ($this->method == 'soap') {
      $options['header'][] = 'Content-Type: text/xml; charset=utf-8';
      $options['header'][] = 'SOAPAction: "' . $this->soapAction . $action . '"';
      $postdata = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
      $postdata .= "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
      $postdata .= " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"";
      $postdata .= " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n";
      $postdata .= "<soap:Body>\n";
      $postdata .= "\t<{$action} xmlns=\"{$this->soapAction}\">\n";
      $postdata .= "\t\t<ApiKey>{$this->api}</ApiKey>\n";
      if (isset($options['params'])) {
        $postdata .= $this
          ->array2xml($options['params'], "\t\t");
      }
      $postdata .= "\t</{$action}>\n";
      $postdata .= "</soap:Body>\n";
      $postdata .= "</soap:Envelope>";
      $method = 'POST';

      //curl_setopt( $ch, CURLOPT_POST, 1 );

      //curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
    }
    else {
      $postdata = "ApiKey={$this->api}";
      $url .= "/{$action}";

      // NOTE: since this is GET, the assumption is that params is a set of simple key-value pairs.
      if (isset($options['params'])) {
        foreach ($options['params'] as $k => $v) {
          $postdata .= '&' . $k . '=' . rawurlencode(utf8_encode($v));
        }
      }
      if ($this->method == 'get') {
        $url .= '?' . $postdata;
        $postdata = '';
      }
      else {
        $options['header'][] = 'Content-Type: application/x-www-form-urlencoded';
        $method = 'POST';

        //curl_setopt( $ch, CURLOPT_POST, 1 );

        //curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
      }
    }
    $res = '';

    // WARNING: using fopen() does not recognize stream contexts in PHP 4.x, so
    // my guess is using fopen() in PHP 4.x implies that POST is not supported
    // (otherwise, how do you tell fopen() to use POST?). tried fsockopen(), but
    // response time was terrible. if someone has more experience with working
    // directly with streams, please troubleshoot that.
    // NOTE: fsockopen() needs a small timeout to force the socket to close.
    // it's defined in SOCKET_TIMEOUT.
    // preferred method is curl, only if it exists and $this->curl is true.
    if ($this->curl && $this->curlExists) {
      $ch = curl_init();
      if ($this->method != 'get') {
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
      }
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_HTTPHEADER, $options['header']);
      curl_setopt($ch, CURLOPT_HEADER, $this->show_response_headers);

      // except for the response, all other information will be stored when debugging is on.
      $res = curl_exec($ch);
      if ($this->debug_level) {
        $this->debug_url = $url;
        $this->debug_request = $postdata;
        $this->debug_info = curl_getinfo($ch);
        $this->debug_info['headers_sent'] = $options['header'];
      }
      $this->debug_response = $res;
      curl_close($ch);
    }
    else {

      // 'header' is actually the entire HTTP payload. as such, you need
      // Content-Length header, otherwise you'll get errors returned/emitted.
      $postLen = strlen($postdata);
      $ctx = array(
        'method' => $method,
        'header' => implode("\n", $options['header']) . "\nContent-Length: " . $postLen . "\n\n" . $postdata,
      );
      if ($this->debug_level) {
        $this->debug_url = $url;
        $this->debug_request = $postdata;
        $this->debug_info['overview'] = 'Used stream_context_create()/fopen() to make request. Content length=' . $postLen;
        $this->debug_info['headers_sent'] = $options['header'];

        //$this->debug_info['complete_content'] = $ctx;
      }
      $pv = PHPVER;

      // the preferred non-cURL way if user is using PHP 5.x
      if ($pv[0] == '5') {
        $context = stream_context_create(array(
          'http' => $ctx,
        ));
        $fp = fopen($url, 'r', false, $context);
        ob_start();
        fpassthru($fp);
        fclose($fp);
        $res = ob_get_clean();
      }
      else {

        // this should work with PHP 4, but it seems to take forever to get data back this way
        // NOTE: setting the default_socket_timeout seems to alleviate this issue [finally].
        list($protocol, $url) = explode('//', $url, 2);
        list($domain, $path) = explode('/', $url, 2);
        $fp = fsockopen($domain, 80, $tvar, $tvar2, SOCKET_TIMEOUT);
        if ($fp) {
          $payload = "{$method} /{$path} HTTP/1.1\n" . "Host: {$domain}\n" . $ctx['header'];
          fwrite($fp, $payload);

          // even with the socket timeout set, using fgets() isn't playing nice, but
          // fpassthru() seems to be doing the right thing.
          ob_start();
          fpassthru($fp);
          list($headers, $res) = explode("\r\n\r\n", ob_get_clean(), 2);
          if ($this->debug_level) {
            $this->debug_info['headers_received'] = $headers;
          }
          fclose($fp);
        }
        elseif ($this->debug_level) {
          $this->debug_info['overview'] .= "\nOpening {$domain}/{$path} failed!";
        }
      }
    }
    if ($res) {
      if ($this->method == 'soap') {
        $tmp = $this
          ->xml2array($res, '/soap:Envelope/soap:Body');
        if (!is_array($tmp)) {
          return $tmp;
        }
        else {
          return $tmp[$action . 'Response'][$action . 'Result'];
        }
      }
      else {
        return $this
          ->xml2array($res);
      }
    }
    else {
      return null;
    }
  }

  /**
   * Convert the given XML $contents into a PHP array. Based on code from:
   * http://www.bin-co.com/php/scripts/xml2array/
   * @param $contents The XML to be converted.
   * @param $root The path of the root element within the XML at which
   * conversion should occur.
   * @param $charset The character set to use.
   * @param $get_attributes 0 or 1. If this is 1 the function will get the
   * attributes as well as the tag values - this results in a different array
   * structure in the return value.
   * @param $priority Can be 'tag' or 'attribute'. This will change the structure
   * of the resulting array. For 'tag', the tags are given more importance.
   * @return A PHP array representing the XML $contents passed in
   */
  function xml2array($contents, $root = '/', $charset = 'utf-8', $get_attributes = 0, $priority = 'tag') {
    if (!$contents) {
      return array();
    }
    if (!function_exists('xml_parser_create')) {
      return array();
    }

    // Get the PHP XML parser
    $parser = xml_parser_create($charset);

    // Attempt to find the last tag in the $root path and use this as the
    // start/end tag for the process of extracting the xml
    // Example input: '/soap:Envelope/soap:Body'
    // Toggles whether the extraction of xml into the array actually occurs
    $extract_on = TRUE;
    $start_and_end_element_name = '';
    $root_elements = explode('/', $root);
    if ($root_elements != FALSE && !empty($root_elements)) {
      $start_and_end_element_name = trim(end($root_elements));
      if (!empty($start_and_end_element_name)) {
        $extract_on = FALSE;
      }
    }
    xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");

    # http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
    xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
    xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
    xml_parse_into_struct($parser, trim($contents), $xml_values);
    xml_parser_free($parser);
    if (!$xml_values) {
      return;
    }
    $xml_array = array();
    $parents = array();
    $opened_tags = array();
    $arr = array();
    $current =& $xml_array;

    // Reference
    // Go through the tags.
    $repeated_tag_index = array();

    // Multiple tags with same name will be turned into an array
    foreach ($xml_values as $data) {
      unset($attributes, $value);

      // Remove existing values, or there will be trouble
      // This command will extract these variables into the foreach scope
      // tag(string), type(string), level(int), attributes(array).
      extract($data);
      if (!empty($start_and_end_element_name) && $tag == $start_and_end_element_name) {

        // Start at the next element (if looking at the opening tag),
        // or don't process any more elements (if looking at the closing tag)...
        $extract_on = !$extract_on;
        continue;
      }
      if (!$extract_on) {
        continue;
      }
      $result = array();
      $attributes_data = array();
      if (isset($value)) {
        if ($priority == 'tag') {
          $result = $value;
        }
        else {
          $result['value'] = $value;
        }

        //Put the value in a assoc array if we are in the 'Attribute' mode
      }

      // Set the attributes too.
      if (isset($attributes) and $get_attributes) {
        foreach ($attributes as $attr => $val) {
          if ($priority == 'tag') {
            $attributes_data[$attr] = $val;
          }
          else {
            $result['attr'][$attr] = $val;
          }

          // Set all the attributes in a array called 'attr'
        }
      }

      // See tag status and do the needed.
      if ($type == "open") {

        // The starting of the tag '<tag>'
        $parent[$level - 1] =& $current;
        if (!is_array($current) or !in_array($tag, array_keys($current))) {

          //Insert New tag
          $current[$tag] = $result;
          if ($attributes_data) {
            $current[$tag . '_attr'] = $attributes_data;
          }
          $repeated_tag_index[$tag . '_' . $level] = 1;
          $current =& $current[$tag];
        }
        else {

          // There was another element with the same tag name
          if (isset($current[$tag][0])) {

            // If there is a 0th element it is already an array
            $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
            $repeated_tag_index[$tag . '_' . $level]++;
          }
          else {

            // This section will make the value an array if multiple tags with the same name appear together
            $current[$tag] = array(
              $current[$tag],
              $result,
            );

            // This will combine the existing item and the new item together to make an array
            $repeated_tag_index[$tag . '_' . $level] = 2;
            if (isset($current[$tag . '_attr'])) {

              // The attribute of the last(0th) tag must be moved as well
              $current[$tag]['0_attr'] = $current[$tag . '_attr'];
              unset($current[$tag . '_attr']);
            }
          }
          $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
          $current =& $current[$tag][$last_item_index];
        }
      }
      elseif ($type == "complete") {

        // Tags that ends in 1 line '<tag />'
        // See if the key is already taken.
        if (!isset($current[$tag])) {

          //New Key

          // Don't insert an empty array - we don't want it!
          if (!(is_array($result) && empty($result))) {
            $current[$tag] = $result;
          }
          $repeated_tag_index[$tag . '_' . $level] = 1;
          if ($priority == 'tag' and $attributes_data) {
            $current[$tag . '_attr'] = $attributes_data;
          }
        }
        else {

          // If taken, put all things inside a list(array)
          if (isset($current[$tag][0]) and is_array($current[$tag])) {

            // If it is already an array...
            // ...push the new element into that array.
            $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
            if ($priority == 'tag' and $get_attributes and $attributes_data) {
              $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
            }
            $repeated_tag_index[$tag . '_' . $level]++;
          }
          else {

            // If it is not an array...
            $current[$tag] = array(
              $current[$tag],
              $result,
            );

            // ...Make it an array using using the existing value and the new value
            $repeated_tag_index[$tag . '_' . $level] = 1;
            if ($priority == 'tag' and $get_attributes) {
              if (isset($current[$tag . '_attr'])) {

                // The attribute of the last(0th) tag must be moved as well
                $current[$tag]['0_attr'] = $current[$tag . '_attr'];
                unset($current[$tag . '_attr']);
              }
              if ($attributes_data) {
                $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
              }
            }
            $repeated_tag_index[$tag . '_' . $level]++;

            // 0 and 1 index is already taken
          }
        }
      }
      elseif ($type == 'close') {

        // End of tag '</tag>'
        $current =& $parent[$level - 1];
      }
    }
    return $xml_array;
  }

  /**
   * Converts an array to XML. This is the inverse to xml2array(). Values
   * are automatically escaped with htmlentities(), so you don't need to escape
   * values ahead of time. If you have, just set the third parameter to false.
   * This is an all-or-nothing deal.
   *
   * @param mixed $arr The associative to convert to an XML fragment
   * @param string $indent (Optional) Starting identation of each element
   * @param string $escape (Optional) Determines whether or not to escape a text node.
   * @return string An XML fragment.
   */
  function array2xml($arr, $indent = '', $escape = true) {
    $buff = '';
    foreach ($arr as $k => $v) {
      if (!is_array($v)) {
        $buff .= "{$indent}<{$k}>" . ($escape ? utf8_encode($v) : $v) . "</{$k}>\n";
      }
      else {

        /*
        Encountered a list. The primary difference between the two branches is that
        in the 'if' branch, a $k element is generated for each item in $v, whereas
        in the 'else' branch, a single $k element encapsulates $v.
        */
        if (isset($v[0])) {
          foreach ($v as $_k => $_v) {
            if (is_array($_v)) {
              $buff .= "{$indent}<{$k}>\n" . $this
                ->array2xml($_v, $indent . "\t", $escape) . "{$indent}</{$k}>\n";
            }
            else {
              $buff .= "{$indent}<{$k}>" . ($escape ? utf8_encode($_v) : $_v) . "</{$k}>\n";
            }
          }
        }
        else {
          $buff .= "{$indent}<{$k}>\n" . $this
            ->array2xml($v, $indent . "\t", $escape) . "{$indent}</{$k}>\n";
        }
      }
    }
    return $buff;
  }

}

/**
* The new CampaignMonitor class that now extends from CMBase. This should be 
* backwards compatible with the original (PHP5) version.
*
* @package CampaignMonitorLib
* @subpackage CampaignMonitor
* @version 1.4.3
* @author Kaiser Shahid <knitcore@yahoo.com> (www.qaiser.net) and 
* Campaign Monitor <support@campaignmonitor.com> 
* @copyright 2007-2009
* @see http://www.campaignmonitor.com/api/
*/
class CampaignMonitor extends CMBase {
  var $url = 'http://api.createsend.com/api/api.asmx', $soapAction = 'http://api.createsend.com/api/';

  /**
   * @param string $api Your API key.
   * @param string $client The default ClientId you're going to work with.
   * @param string $campaign The default CampaignId you're going to work with.
   * @param string $list The default ListId you're going to work with.
   * @param string $method Determines request type. Values are either get, post, or soap.
   */
  function CampaignMonitor($api = null, $client = null, $campaign = null, $list = null, $method = 'get') {
    CMBase::CMBase($api, $client, $campaign, $list, $method);
  }

  /**
   * Wrapper for Subscribers.GetActive. This method triples as Subscribers.GetUnsubscribed
   * and Subscribers.GetBounced when the very last parameter is overridden.
   *
   * @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param string $action (Optional) Set the actual API method to call. Defaults to Subscribers.GeActive if no other valid value is given.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetActive.aspx
   */
  function subscribersGetActive($date = 0, $list_id = null, $action = 'Subscribers.GetActive') {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    if (is_numeric($date)) {
      $date = date('Y-m-d H:i:s', $date);
    }
    $valid_actions = array(
      'Subscribers.GetActive' => '',
      'Subscribers.GetUnsubscribed' => '',
      'Subscribers.GetBounced' => '',
    );
    if (!isset($valid_actions[$action])) {
      $action = 'Subscribers.GetActive';
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Date' => $date,
      ),
    ));
  }

  /**
   * @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetUnsubscribed.aspx
   */
  function subscribersGetUnsubscribed($date = 0, $list_id = null) {
    return $this
      ->subscribersGetActive($date, $list_id, 'Subscribers.GetUnsubscribed');
  }

  /**
   * @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetBounced.aspx
   */
  function subscribersGetBounced($date = 0, $list_id = null) {
    return $this
      ->subscribersGetActive($date, $list_id, 'Subscribers.GetBounced');
  }

  /**
   * subscriberAdd()
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
   * @see http://www.campaignmonitor.com/api/Subscriber.Add.aspx
   */
  function subscriberAdd($email, $name, $list_id = null, $resubscribe = false) {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    $action = 'Subscriber.Add';
    if ($resubscribe) {
      $action = 'Subscriber.AddAndResubscribe';
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Email' => $email,
        'Name' => $name,
      ),
    ));
  }

  /**
   * This encapsulates the check of whether this particular user unsubscribed once.
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   */
  function subscriberAddRedundant($email, $name, $list_id = null) {
    $added = $this
      ->subscriberAdd($email, $name, $list_id);
    if ($added && $added['Result']['Code'] == '204') {
      $subscribed = $this
        ->subscribersGetIsSubscribed($email, $list_id);

      // Must have unsubscribed, so resubscribe
      if ($subscribed['anyType'] == 'False') {

        // since we're internal, we'll just call the method with full parameters rather
        // than go through a secondary wrapper function.
        $added = $this
          ->subscriberAdd($email, $name, $list_id, true);
        return $added;
      }
    }
    return $added;
  }

  /**
   * @param string $email Email address.
   * @param string $name User's name.
   * @param mixed $fields Should be a $key => $value mapping. If there are more than one items for $key, let
   *        $value be a list of scalar values. Example: array( 'Interests' => array( 'xbox', 'wii' ) )
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscriber.AddWithCustomFields.aspx
   */
  function subscriberAddWithCustomFields($email, $name, $fields, $list_id = null, $resubscribe = false) {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    $action = 'Subscriber.AddWithCustomFields';
    if ($resubscribe) {
      $action = 'Subscriber.AddAndResubscribeWithCustomFields';
    }
    if (!is_array($fields)) {
      $fields = array();
    }
    $_fields = array(
      'SubscriberCustomField' => array(),
    );
    foreach ($fields as $k => $v) {
      if (is_array($v)) {
        foreach ($v as $nv) {
          $_fields['SubscriberCustomField'][] = array(
            'Key' => $k,
            'Value' => $nv,
          );
        }
      }
      else {
        $_fields['SubscriberCustomField'][] = array(
          'Key' => $k,
          'Value' => $v,
        );
      }
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Email' => $email,
        'Name' => $name,
        'CustomFields' => $_fields,
      ),
    ));
  }

  /**
   * Same as subscriberAddRedundant() except with CustomFields.
   *
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   */
  function subscriberAddWithCustomFieldsRedundant($email, $name, $fields, $list_id = null) {
    $added = $this
      ->subscriberAddWithCustomFields($email, $name, $fields, $list_id);
    if ($added && $added['Code'] == '0') {
      $subscribed = $this
        ->subscribersGetIsSubscribed($email);
      if ($subscribed == 'False') {
        $added = $this
          ->subscriberAddWithCustomFields($email, $name, $fields, $list_id, true);
        return $added;
      }
    }
    return $added;
  }

  /**
   * @param string $email Email address.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $check_subscribed If true, does the Subscribers.GetIsSubscribed API method instead.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscriber.Unsubscribe.aspx
   */
  function subscriberUnsubscribe($email, $list_id = null, $check_subscribed = false) {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    $action = 'Subscriber.Unsubscribe';
    if ($check_subscribed) {
      $action = 'Subscribers.GetIsSubscribed';
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Email' => $email,
      ),
    ));
  }

  /**
   * @return string A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetIsSubscribed.aspx
   */
  function subscribersGetIsSubscribed($email, $list_id = null) {
    return $this
      ->subscriberUnsubscribe($email, $list_id, true);
  }

  /**
   * Given an array of lists, indicate whether the $email is subscribed to each of those lists.
   *
   * @param string $email User's email
   * @param mixed $lists An associative array of lists to check against. Each key should be a List ID
   * @param boolean $no_assoc If true, only returns an array where each value indicates that the user is subscribed
   *        to that particular list. Otherwise, returns a fully associative array of $list_id => true | false.
   * @return mixed An array corresponding to $lists where true means the user is subscribed to that particular list.
   */
  function checkSubscriptions($email, $lists, $no_assoc = true) {
    $nlist = array();
    foreach ($lists as $lid => $misc) {
      $val = $this
        ->subscribersGetIsSubscribed($email, $lid);
      $val = $val != 'False';
      if ($no_assoc && $val) {
        $nlist[] = $lid;
      }
      elseif (!$no_assoc) {
        $nlist[$lid] = $val;
      }
    }
    return $nlist;
  }

  /**
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @see http://www.campaignmonitor.com/api/Subscriber.AddAndResubscribe.aspx
   */
  function subscriberAddAndResubscribe($email, $name, $list_id = null) {
    return $this
      ->subscriberAdd($email, $name, $list_id, true);
  }

  /**
   * @param string $email Email address.
   * @param string $name User's name.
   * @param mixed $fields Should only be a single-dimension array of key-value pairs.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscriber.AddAndResubscribeWithCustomFields.aspx
   */
  function subscriberAddAndResubscribeWithCustomFields($email, $name, $fields, $list_id = null) {
    return $this
      ->subscriberAddWithCustomFields($email, $name, $fields, $list_id, true);
  }

  /**
   * Returns the details of a particular subscriber.
   * @param $list_id The ID of the list to which the subscriber belongs
   * @param $email The subscriber's email address
   * @return mixed A parsed response from the server, or null if something failed
   * @see http://www.campaignmonitor.com/api/method/subscribers-get-single-subscriber/
   */
  function subscriberGetSingleSubscriber($list_id = null, $email) {
    if (!$list_id != null) {
      $list_id = $this->list_id;
    }
    return $this
      ->makeCall('Subscribers.GetSingleSubscriber', array(
      'params' => array(
        'ListID' => $list_id,
        'EmailAddress' => $email,
      ),
    ));
  }

  /*
   * A generic wrapper to feed Client.* calls.
   *
   * @param string $method The API method to call.
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   */
  function clientGeneric($method, $client_id = null) {
    if (!$client_id) {
      $client_id = $this->client_id;
    }
    return $this
      ->makeCall('Client.' . $method, array(
      'params' => array(
        'ClientID' => $client_id,
      ),
    ));
  }

  /**
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Client.GetLists.aspx
   */
  function clientGetLists($client_id = null) {
    return $this
      ->clientGeneric('GetLists', $client_id);
  }

  /**
   * Creates an associative array with list_id => List_label pairings.
   *
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   */
  function clientGetListsDropdown($client_id = null) {
    $lists = $this
      ->clientGetLists($client_id);
    if (!isset($lists['List'])) {
      return null;
    }
    else {
      $lists = $lists['List'];
    }
    $_lists = array();
    if (isset($lists[0])) {
      foreach ($lists as $list) {
        $_lists[$list['ListID']] = $list['Name'];
      }
    }
    else {
      $_lists[$lists['ListID']] = $lists['Name'];
    }
    return $_lists;
  }

  /**
   * Creates an associative array with list_id:List_Label => (list_id) List_label pairings.
   * Remember that you'll need to split the key on ':' only once to get the appropriate ListID
   * and Segment Name.
   *
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   */
  function clientGetSegmentsDropdown($client_id = null) {
    $lists = $this
      ->clientGetSegments($client_id);
    if (!isset($lists['List'])) {
      return null;
    }
    else {
      $lists = $lists['List'];
    }
    $_lists = array();
    if (isset($lists[0])) {
      foreach ($lists as $list) {
        $_lists[$list['ListID'] . ':' . $list['Name']] = '(' . $list['ListID'] . ') ' . $list['Name'];
      }
    }
    else {
      $_lists[$lists['ListID'] . ':' . $lists['Name']] = '(' . $lists['ListID'] . ') ' . $lists['Name'];
    }
    return $_lists;
  }

  /**
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Client.GetCampaigns.aspx
   */
  function clientGetCampaigns($client_id = null) {
    return $this
      ->clientGeneric('GetCampaigns', $client_id);
  }

  /**
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Client.GetSegments.aspx
   */
  function clientGetSegments($client_id = null) {
    return $this
      ->clientGeneric('GetSegments', $client_id);
  }

  /**
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/client-getsuppressionlist/
   */
  function clientGetSuppressionList($client_id = null) {
    return $this
      ->clientGeneric('GetSuppressionList', $client_id);
  }

  /**
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/client-gettemplates/
   */
  function clientGetTemplates($client_id = null) {
    return $this
      ->clientGeneric('GetTemplates', $client_id);
  }

  /**
   * @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/client-getdetail/
   */
  function clientGetDetail($client_id = null) {
    return $this
      ->clientGeneric('GetDetail', $client_id);
  }

  /**
   * @param string $companyName (CompanyName) Company name of the client to be added
   * @param string $contactName (ContactName) Contact name of the client to be added
   * @param string $emailAddress (EmailAddress) Email Address of the client to be added
   * @param string $country (Country) Country of the client to be added
   * @param string $timezone (Timezone) Timezone of the client to be added
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/client-create/
   */
  function clientCreate($companyName, $contactName, $emailAddress, $country, $timezone) {
    return $this
      ->makeCall('Client.Create', array(
      'params' => array(
        'CompanyName' => $companyName,
        'ContactName' => $contactName,
        'EmailAddress' => $emailAddress,
        'Country' => $country,
        'Timezone' => $timezone,
      ),
    ));
  }

  /**
   * @param int $client_id (ClientID) ID of the client to be updated
   * @param string $companyName (CompanyName) Company name of the client to be updated
   * @param string $contactName (ContactName) Contact name of the client to be updated
   * @param string $emailAddress (EmailAddress) Email Address of the client to be updated
   * @param string $country (Country) Country of the client to be updated
   * @param string $timezone (Timezone) Timezone of the client to be updated
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/client-create/
   */
  function clientUpdateBasics($client_id, $companyName, $contactName, $emailAddress, $country, $timezone) {
    return $this
      ->makeCall('Client.UpdateBasics', array(
      'params' => array(
        'ClientID' => $client_id,
        'CompanyName' => $companyName,
        'ContactName' => $contactName,
        'EmailAddress' => $emailAddress,
        'Country' => $country,
        'Timezone' => $timezone,
      ),
    ));
  }

  /**
   * @param int $client_id (ClientID) ID of the client to be updated
   * @param string $accessLevel (AccessLevel) AccessLevel of the client
   * @param string $username (Username) Clients username
   * @param string $password (Password) Password of the client
   * @param string $billingType (BillingType) BillingType that the client will be set as
   * @param string $currency (Currency) Currency that the client will pay in
   * @param string $deliveryFee (DeliveryFee) Per campaign deliivery fee for the campaign
   * @param string $costPerRecipient (CostPerRecipient) Per email fee for the client
   * @param string $designAndSpamTestFee (DesignAndSpamTestFee) Amount the client will
   *				be charged if they have access to send design/spam tests
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/client-updateaccessandbilling/
   */
  function clientUpdateAccessAndBilling($client_id, $accessLevel, $username, $password, $billingType, $currency, $deliveryFee, $costPerRecipient, $designAndSpamTestFee) {
    return $this
      ->makeCall('Client.UpdateAccessAndBilling', array(
      'params' => array(
        'ClientID' => $client_id,
        'AccessLevel' => $accessLevel,
        'Username' => $username,
        'Password' => $password,
        'BillingType' => $billingType,
        'Currency' => $currency,
        'DeliveryFee' => $deliveryFee,
        'CostPerRecipient' => $costPerRecipient,
        'DesignAndSpamTestFee' => $designAndSpamTestFee,
      ),
    ));
  }

  /**
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/User.GetClients.aspx
   */
  function userGetClients() {
    return $this
      ->makeCall('User.GetClients');
  }

  /**
   * @return string A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/User.GetSystemDate.aspx
   */
  function userGetSystemDate() {
    return $this
      ->makeCall('User.GetSystemDate');
  }

  /**
   * @return string A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/user-gettimezones/
   */
  function userGetTimezones() {
    return $this
      ->makeCall('User.GetTimezones');
  }

  /**
   * @return string A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/user-getcountries/
   */
  function userGetCountries() {
    return $this
      ->makeCall('User.GetCountries');
  }

  
  function userGetApiKey($site_url, $username, $password) {
    return $this
      ->makeCall('User.GetApiKey', array(
      'params' => array(
        'SiteUrl' => $site_url,
        'Username' => $username,
        'Password' => $password,
      ),
    ));
  }

  /**
   * A generic wrapper to feed Campaign.* calls.
   *
   * @param string $method The API method to call.
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   */
  function campaignGeneric($method, $campaign_id = null) {
    if (!$campaign_id) {
      $campaign_id = $this->campaign_id;
    }
    return $this
      ->makeCall('Campaign.' . $method, array(
      'params' => array(
        'CampaignID' => $campaign_id,
      ),
    ));
  }

  /**
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.GetSummary.aspx
   */
  function campaignGetSummary($campaign_id = null) {
    return $this
      ->campaignGeneric('GetSummary', $campaign_id);
  }

  /**
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.GetOpens.aspx
   */
  function campaignGetOpens($campaign_id = null) {
    return $this
      ->campaignGeneric('GetOpens', $campaign_id);
  }

  /**
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.GetBounces.aspx
   */
  function campaignGetBounces($campaign_id = null) {
    return $this
      ->campaignGeneric('GetBounces', $campaign_id);
  }

  /**
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.GetSubscriberClicks.aspx
   */
  function campaignGetSubscriberClicks($campaign_id = null) {
    return $this
      ->campaignGeneric('GetSubscriberClicks', $campaign_id);
  }

  /**
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.GetUnsubscribes.aspx
   */
  function campaignGetUnsubscribes($campaign_id = null) {
    return $this
      ->campaignGeneric('GetUnsubscribes', $campaign_id);
  }

  /**
   * @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.GetLists.aspx
   */
  function campaignGetLists($campaign_id = null) {
    return $this
      ->campaignGeneric('GetLists', $campaign_id);
  }

  /**
   * @param int $client_id The ClientID you wish to use; set it to null to use the default class property.
   * @param string $name (CampaignName) Name of campaign
   * @param string $subject (CampaignSubject) Subject of campaign mailing
   * @param string $fromName (FromName) The From name of the sender
   * @param string $fromEmail (FromEmail) The email of the sender
   * @param string $replyTo (ReplyTo) An alternate email to send replies to
   * @param string $htmlUrl (HtmlUrl) Location of HTML body of email
   * @param string $textUrl (TextUrl) Location of plaintext body of email
   * @param array $subscriberListIds (SubscriberListIDs) An array of ListIDs. This will automatically be converted to the right format
   * @param array $listSegments (ListSegments) An array of segment names and their corresponding ListIDs. Each element needs to
   *        be an associative array with keys ListID and Name.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Campaign.Create.aspx
   */
  function campaignCreate($client_id, $name, $subject, $fromName, $fromEmail, $replyTo, $htmlUrl, $textUrl, $subscriberListIds, $listSegments) {
    if ($client_id == null) {
      $client_id = $this->client_id;
    }
    $_subListIds = '';
    if ($subscriberListIds != "") {
      $_subListIds = array(
        'string' => array(),
      );
      if (is_array($subscriberListIds)) {
        foreach ($subscriberListIds as $lid) {
          $_subListIds['string'][] = $lid;
        }
      }
    }
    $_seg = '';
    if ($listSegments != "") {
      $_seg = array(
        'List' => array(),
      );
      if (is_array($listSegments)) {
        foreach ($listSegments as $seg) {
          $_seg['List'][] = $seg;
        }
      }
    }
    return $this
      ->makeCall('Campaign.Create', array(
      'params' => array(
        'ClientID' => $client_id,
        'CampaignName' => $name,
        'CampaignSubject' => $subject,
        'FromName' => $fromName,
        'FromEmail' => $fromEmail,
        'ReplyTo' => $replyTo,
        'HtmlUrl' => $htmlUrl,
        'TextUrl' => $textUrl,
        'SubscriberListIDs' => $_subListIds,
        'ListSegments' => $_seg,
      ),
    ));
  }

  /**
   * @param int $client_id The CampaignID you wish to use; set it to null to use the default class property
   * @param string $confirmEmail (ConfirmationEmail) Email address to send confirmation of campaign send to
   * @param string $sendDate (SendDate) The timestamp to send the campaign. It must be formatted as YYY-MM-DD HH:MM:SS
   *               and should correspond to user's timezone.
   */
  function campaignSend($campaign_id, $confirmEmail, $sendDate) {
    if ($campaign_id == null) {
      $campaign_id = $this->campaign_id;
    }
    return $this
      ->makeCall('Campaign.Send', array(
      'params' => array(
        'CampaignID' => $campaign_id,
        'ConfirmationEmail' => $confirmEmail,
        'SendDate' => $sendDate,
      ),
    ));
  }

  /**
   * Delete a campaign.
   * @param $campaign_id The ID of the campaign to delete.
   * @return A Status code indicating success or failure.
   * @see http://www.campaignmonitor.com/api/method/campaign-delete/
   */
  function campaignDelete($campaign_id) {
    return $this
      ->campaignGeneric('Delete', $campaign_id);
  }

  /**
  * @param int $client_id (ClientID) ID of the client the list will be created for
  * @param string $title (Title) Name of the new list
  * @param string $unsubscribePage (UnsubscribePage) URL of the page users will be
  *				directed to when they unsubscribe from this list.
  * @param string $confirmOptIn (ConfirmOptIn) If true, the user will be sent a confirmation
  *				email before they are added to the list. If they click the link to confirm
  *				their subscription they will be added to the list. If false, they will be
  *				added automatically.
  * @param string $confirmationSuccessPage (ConfirmationSuccessPage) URL of the page that
  *				users will be sent to if they confirm their subscription. Only required when
  				$confirmOptIn is true.
  * @see http://www.campaignmonitor.com/api/method/list-create/
  */
  function listCreate($client_id, $title, $unsubscribePage, $confirmOptIn, $confirmationSuccessPage) {
    if ($confirmOptIn == 'false') {
      $confirmationSuccessPage = '';
    }
    return $this
      ->makeCall('List.Create', array(
      'params' => array(
        'ClientID' => $client_id,
        'Title' => $title,
        'UnsubscribePage' => $unsubscribePage,
        'ConfirmOptIn' => $confirmOptIn,
        'ConfirmationSuccessPage' => $confirmationSuccessPage,
      ),
    ));
  }

  /**
  * @param int $list_id (List) ID of the list to be updated
  * @param string $title (Title) Name of the new list
  * @param string $unsubscribePage (UnsubscribePage) URL of the page users will be
  *				directed to when they unsubscribe from this list.
  * @param string $confirmOptIn (ConfirmOptIn) If true, the user will be sent a confirmation
  *				email before they are added to the list. If they click the link to confirm
  *				their subscription they will be added to the list. If false, they will be
  *				added automatically.
  * @param string $confirmationSuccessPage (ConfirmationSuccessPage) URL of the page that
  *				users will be sent to if they confirm their subscription. Only required when
  				$confirmOptIn is true.
  * @see http://www.campaignmonitor.com/api/method/list-update/
  */
  function listUpdate($list_id, $title, $unsubscribePage, $confirmOptIn, $confirmationSuccessPage) {
    if ($confirmOptIn == 'false') {
      $confirmationSuccessPage = '';
    }
    return $this
      ->makeCall('List.Update', array(
      'params' => array(
        'ListID' => $list_id,
        'Title' => $title,
        'UnsubscribePage' => $unsubscribePage,
        'ConfirmOptIn' => $confirmOptIn,
        'ConfirmationSuccessPage' => $confirmationSuccessPage,
      ),
    ));
  }

  /**
   * @param int $list_id (List) ID of the list to be deleted
   * @see http://www.campaignmonitor.com/api/method/list-delete/
   */
  function listDelete($list_id) {
    return $this
      ->makeCall('List.Delete', array(
      'params' => array(
        'ListID' => $list_id,
      ),
    ));
  }

  /**
   * @param int $list_id (List) ID of the list to be deleted
   * @see http://www.campaignmonitor.com/api/method/list-getdetail/
   */
  function listGetDetail($list_id) {
    return $this
      ->makeCall('List.GetDetail', array(
      'params' => array(
        'ListID' => $list_id,
      ),
    ));
  }

  /**
   * Gets statistics for a subscriber list
   * @param $list_id The ID of the list whose statistics will be returned.
   * @return mixed A parsed response from the server, or null if something
   * @see http://www.campaignmonitor.com/api/method/list-getstats/
   */
  function listGetStats($list_id) {
    return $this
      ->makeCall('List.GetStats', array(
      'params' => array(
        'ListID' => $list_id,
      ),
    ));
  }

  
  function listCreateCustomField($list_id, $fieldName, $dataType, $options) {
    if ($dataType == 'Text' || $dataType == 'Number') {
      $options = null;
    }
    return $this
      ->makeCall('List.CreateCustomField', array(
      'params' => array(
        'ListID' => $list_id,
        'FieldName' => $fieldName,
        'DataType' => $dataType,
        'Options' => $options,
      ),
    ));
  }

  /**
   * @param int $list_id (ListID) A valid list ID to check against.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/list-getcustomfields/
   */
  function listGetCustomFields($list_id) {
    return $this
      ->makeCall('List.GetCustomFields', array(
      'params' => array(
        'ListID' => $list_id,
      ),
    ));
  }

  /**
   * @param int $list_id (ListID) A valid list ID to check against.
   * @param int $key (Key) The Key of the field we want to delete.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/method/list-deletecustomfield/
   */
  function listDeleteCustomField($list_id, $key) {
    return $this
      ->makeCall('List.DeleteCustomField', array(
      'params' => array(
        'ListID' => $list_id,
        'Key' => $key,
      ),
    ));
  }

  /**
   * @param int $client_id (ClientID) ID of the client the template will be created for
   * @param string $template_name (TemplateName) Name of the new template
   * @param string $html_url (HTMLPageURL) URL of the HTML page you have created for the template
   * @param string $zip_url (ZipFileURL) URL of a zip file containing any other files required by the template
   * @param string $screenshot_url (ScreenshotURL) URL of a screenshot of the template
   * @see http://www.campaignmonitor.com/api/method/template-create/
   */
  function templateCreate($client_id, $template_name, $html_url, $zip_url, $screenshot_url) {
    return $this
      ->makeCall('Template.Create', array(
      'params' => array(
        'ClientID' => $client_id,
        'TemplateName' => $template_name,
        'HTMLPageURL' => $html_url,
        'ZIPFileURL' => $zip_url,
        'ScreenshotURL' => $screenshot_url,
      ),
    ));
  }

  /**
   * @param string $template_id (TemplateID) ID of the template whose details are being requested
   * @see http://www.campaignmonitor.com/api/method/template-getdetail/
   */
  function templateGetDetail($template_id) {
    return $this
      ->makeCall('Template.GetDetail', array(
      'params' => array(
        'TemplateID' => $template_id,
      ),
    ));
  }

  /**
   * @param string $template_id (TemplateID) ID of the template to be updated
   * @param string $template_name (TemplateName) Name of the template
   * @param string $html_url (HTMLPageURL) URL of the HTML page you have created for the template
   * @param string $zip_url (ZipFileURL) URL of a zip file containing any other files required by the template
   * @param string $screenshot_url (ScreenshotURL) URL of a screenshot of the template
   * @see http://www.campaignmonitor.com/api/method/template-update/
   */
  function templateUpdate($template_id, $template_name, $html_url, $zip_url, $screenshot_url) {
    return $this
      ->makeCall('Template.Update', array(
      'params' => array(
        'TemplateID' => $template_id,
        'TemplateName' => $template_name,
        'HTMLPageURL' => $html_url,
        'ZIPFileURL' => $zip_url,
        'ScreenshotURL' => $screenshot_url,
      ),
    ));
  }

  /**
   * @param string $template_id (TemplateID) ID of the template to be deleted
   * @see http://www.campaignmonitor.com/api/method/template-delete/
   */
  function templateDelete($template_id) {
    return $this
      ->makeCall('Template.Delete', array(
      'params' => array(
        'TemplateID' => $template_id,
      ),
    ));
  }

}

Constants

Namesort descending Description
PHPVER This is an all-inclusive package for interfacing with Campaign Monitor's services. It supports SOAP, GET, and POST seamlessly (just set the $method property to 'soap', 'get', or 'post' before making a call) and…
SOCKET_TIMEOUT

Classes

Namesort descending Description
CampaignMonitor The new CampaignMonitor class that now extends from CMBase. This should be backwards compatible with the original (PHP5) version.
CMBase