0

I'm trying to connect to the CVENT API using a SOAP Client written in PHP. Below is what I have thus far for my CventClient class which extends the SoapClient. I continue to get an error message stating that my Access Denied. I have verified that the credentials are correct. I've tried rewriting the way that the SoapClient is constructed based off some articles online, but the error message is the same. Any help on this would be greatly appreciated.

<?php
/*
** CventClient.class.php
** Author: github.com/gcanivet
** Description: A php soap client for the Cvent API. 
**
** To Do:
**     - add additional remote functions support
**     - add logging capabilities    
**     - proper docs format
**     
** Notes:
**     - Search - 25,000 IDs limit not handled
**     - CreateContact, UpdateContact, DeleteContact, CreateContactGroup, TransferInvitee, and SendEmail - 200 object limit not handled
**     - GetUpdated - 10,000 IDs limit not handled
** ... Typicaly a limit of 25,000 ids returned, maximum 256 search filter, but depends on Client accounts
**
*/
ini_set('soap.wsdl_cache_enabled', 1); 
ini_set('soap.wsdl_cache_ttl', 86400); //default: 86400, in seconds

class CventClient extends SoapClient {
    public $client;
    public $ServerURL;
    public $CventSessionHeader;
    public $debug = false;
    public $MAX_FILTER_SIZE = 100; # 256 doesn't seem to work.
    public $RESULTS_PER_PAGE = 200;

    public function CventClient() {
    }

    public function Login($acct, $username, $password) {
        $this->client = new SoapDebugClient("https://api.cvent.com/soap/V200611.ASMX?WSDL", array('trace' => true, 'exceptions' => true));
        $cache = true;
        $file = 'cvent_api_session.txt';    
        if($cache && file_exists($file) && time() <= strtotime("+1 hour", $last = filemtime($file))) { 
            # a valid session is cached, use it
            if($this->debug) print 'CventClient::Session already exists.<br/>';
            $data = file_get_contents($file);
            $arr_response = array();
            $arr_response = explode(',', $data);
            $this->ServerURL = $arr_response[0];
            $this->CventSessionHeader = $arr_response[1];                   
        } else { # sessions expire 1hr after CREATION
            if($this->debug) print 'CventClient::Session does not exist. Get new one.<br/>';
            $params = array();
            $params['AccountNumber'] = $acct;
            $params['UserName']     = $username;
            $params['Password']     = $password;

            $response = $this->client->Login($params);
            if(!$response->LoginResult->LoginSuccess) {
                throw new Exception("CventClient:: Login unsuccessful");
            }
            $arr_response = array();
            $arr_response[] = $response->LoginResult->ServerURL;
            $arr_response[] = $response->LoginResult->CventSessionHeader;
            $data = implode(',', $arr_response);
            file_put_contents($file, $data);
            $this->ServerURL = $arr_response[0];
            $this->CventSessionHeader = $arr_response[1];
        }
        $this->client->__setLocation($this->ServerURL);
        $header_body = array('CventSessionValue' => $this->CventSessionHeader);
        $header = new SoapHeader('http://api.cvent.com/2006-11', 'CventSessionHeader', $header_body);
        $this->client->__setSoapHeaders($header);
        if($this->debug) print 'CventClient:: ServerURL: '.$this->ServerURL.', CventSessionHeader: '.$this->CventSessionHeader.'<br/>';
    }

    public function DescribeGlobal() {
        $response = $this->client->DescribeGlobal();
        return $response;
    }

    public function DescribeCvObject($objectTypes) {
        $params->ObjectTypes = $objectTypes;
        $response = $this->client->DescribeCvObject($params);
        return $response;
    }       

    public function GetEventById($eventId) {
        $events = $this->RetrieveEvents($eventId);  
        if(sizeof($events) != 1) throw new Exception('CventClient::GetEventById: EventId '.$eventId.' not found');
        return $events[0];
    }

    public function GetContactById($contactId) {
        $contacts = $this->RetrieveContacts($contactId);    
        if(sizeof($contacts) != 1) throw new Exception('CventClient::GetContactById: ContactId '.$contactId.' not found');
        return $contacts[0];
    }   

    public function GetUpcomingEvents() {
        // needs to be tested
        $v = date('Y-m-d', strtotime('-14 days')).'T00:00:00'; // '2011-10-31T00:00:00';
        $response = $this->SearchByFilter('Event', 'AndSearch', array((object) array('Field' => 'EventStartDate', 'Operator' => 'Greater than', 'Value' => $v)));
        if(isset($response->SearchResult->Id)) return $response->SearchResult->Id;
        return false;
    }

    public function GetNumberOfRegistrations($eventId) {
        // needs to be tested
        $response = $this->SearchByFilter('Registration', 'AndSearch', array((object) array('Field' => 'EventId', 'Operator' => 'Equals', 'Value' => $eventId)));
        if(isset($response->SearchResult->Id)) return count($response->SearchResult->Id);
        return false;
    }

    public function GetNumberOfGuests($eventId) {
        // needs to be tested
        $response = $this->SearchByFilter('Guest', 'AndSearch', array((object) array('Field' => 'EventId', 'Operator' => 'Equals', 'Value' => $eventId)));
        if(isset($response->SearchResult->Id)) return count($response->SearchResult->Id);
        return false;
    }

    public function SearchAllDistributionLists() {
        $response = $this->SearchByFilter('DistributionList', 'OrSearch', array(
                            (object) array('Field' => 'DistributionListName', 'Operator' => 'Equals', 'Value' => 'Something'),
                                (object) array('Field' => 'DistributionListName', 'Operator' => 'Not Equal to', 'Value' => 'Something')));
        if(isset($response->SearchResult->Id)) return $response->SearchResult->Id;
        return false;
    }

    public function SearchContactBySourceId($remaxId) {
        $response = $this->SearchByFilter('Contact', 'AndSearch', array((object) array('Field' => 'SourceId', 'Operator' => 'Equals', 'Value' => $remaxId)));
        if(isset($response->SearchResult->Id)) return $response->SearchResult->Id;
        return false;
    }

    public function SearchContactsByGroupId($groupId) {
        $response = $this->SearchByFilter('Contact', 'AndSearch', array((object) array('Field' => 'GroupId', 'Operator' => 'Equals', 'Value' => $groupId)));
        if(isset($response->SearchResult->Id)) return $response->SearchResult->Id;
        return false;
    }

    public function SearchContactsByDistributionListId($distId) {
        $response = $this->SearchByFilter('Contact', 'AndSearch', array((object) array('Field' => 'DistId', 'Operator' => 'Equals', 'Value' => $distId)));
        if(isset($response->SearchResult->Id)) return $response->SearchResult->Id;
        return false;
    }

    public function SearchContactByDistributionListIdAndEmail($distId, $email) {
        // name implies email is unique
        $response = $this->SearchByFilter('Contact', 'AndSearch', array(
                            (object) array('Field' => 'DistId', 'Operator' => 'Equals', 'Value' => $distId),
                            (object) array('Field' => 'EmailAddress', 'Operator' => 'Equals', 'Value' => $email)));
        if(isset($response->SearchResult->Id)) return $response->SearchResult->Id;
        return false;
    }


    # Retrieve 
    public function RetrieveEvents($ids) {
        return $this->RetrieveAllPages('Event', $ids);
    }

    public function RetrieveContacts($ids) {
        return $this->RetrieveAllPages('Contact', $ids);
    }
    public function RetrieveDistributionLists($ids) {
        return $this->RetrieveAllPages('DistributionList', $ids);
    }
    public function RetrieveContactBySourceId($remaxid) {
        $result = $this->SearchContactBySourceId($remaxid);     
        if($result === false) throw new Exception("CventClient::RetrieveContactBySourceId::$remaxid not found, cannot retrieve");
        $ids[] = $result->SearchResult->Id;
        $result = $this->RetrieveContacts($ids);        
        return $result;
    }

    public function RetrieveContactIdsBySourceIds($sourceIds) {
        // needs to be tested
        // this returns Ids, not Objects, for consistency should be renamed SearchContactsBySourceIds($sourceIds)
        $total = count($sourceIds);
        $contactIds = array();
        for($i = 0; $i < count($sourceIds); $i += $this->MAX_FILTER_SIZE) {
            if ($this->debug) print "CventClient::RetrieveContactIdsBySourceIds:: retrieving contactIds from $i to ".($i + $this->MAX_FILTER_SIZE)."<br/>";
            $batch = array_slice($sourceIds, $i, $i + $this->MAX_FILTER_SIZE);      
            $criteria = NULL;
            $criteria->ObjectType = 'Contact';
            $criteria->CvSearchObject->SearchType = 'OrSearch'; 
            for($j=0; $j < sizeof($batch); $j++) {
                $criteria->CvSearchObject->Filter[$j]->Field = 'SourceId';
                $criteria->CvSearchObject->Filter[$j]->Operator = 'Equals';
                $criteria->CvSearchObject->Filter[$j]->Value = $batch[$j];
            }
            $tmp = $this->client->Search($criteria);
            $tmp = $tmp->SearchResult->Id;
            if(is_array($tmp)) {
                $contactIds = array_merge($contactIds, $tmp);
            } else {
                $contactIds = array_merge($contactIds, array($tmp));
            }
        }
        return $contactIds;         
    }


    # Create or Updates

    public function AddContactsToDistributionList($distId, $contactIds) {
        if(!is_array($contactIds)) $contactIds = array($contactIds); // safety measure, contactId must be CvObject type
        $response = $this->client->ManageDistributionListMembers((object) array('DistListId' => $distId, 'Action' => 'Add', 'CvObjects' => $contactIds));   
        if(isset($response->ManageDistributionListMembersResult->ManageDistributionListResult)) return $response->ManageDistributionListMembersResult->ManageDistributionListResult;
        return false;
    }
    public function AddContactToDistributionListByEmailAddress($distId, $email) {
        $response = $this->SearchByFilter('Contact', 'AndSearch', array((object) array('Field' => 'EmailAddress', 'Operator' => 'Equals', 'Value' => $email)));
        if(!isset($response->SearchResult->Id)) return false;
        $contactId =  $response->SearchResult->Id;
        return $this->AddContactsToDistributionList($distId, array((object) array('Id' => $contactId, 'MessageId' => '')));
    }   
    public function CreateContactAndAddToDistributionList($contact, $distId) {  
        // pre: contact is an array with appropriate contact fields 
        $contacts = array($contact);    
        $results = $this->CreateUpdateContacts('Create', $contacts);
        print "<br/>create contacts<pre>";
        print_r($results);
        print "</pre><br/>";

        if(sizeof($results['passed']) == 1) { // new contact
            $contactId = $results['passed'][0]['result']->Id;
            print "contactId: ".$contactId;
            return $this->AddContactsToDistributionList($distId, array((object) array('Id' => $contactId, 'MessageId' => '')));
        } elseif (sizeof($results['failed']) == 1) { // error adding contact
            $errcode = $results['failed'][0]['result']->Errors->Error[0]->Code;
            if($errcode == 'CV40104' || $errcode == 'CV40103') { // contact already exists | duplicated contact object
                $emailaddress = $results['failed'][0]['contact']['EmailAddress'];
                print "emailaddress: ".$emailaddress;
                return $this->AddContactToDistributionListByEmailAddress($distId, $emailaddress);       
            }
        } 
        return false;
    }


    public function CreateUpdateContacts($type, $contacts) {
        # type = 'Create' or 'Update'
        // this could be improved A LOT
        if(!is_array($contacts)) $contacts = array($contacts);
        $total = sizeof($contacts);
        $pages = ceil($total / $this->RESULTS_PER_PAGE);
        $remainder = $total % $this->RESULTS_PER_PAGE;
        $passed = array();
        $failed = array();
        for($i = 0; $i < $pages; $i++) {
            $x = $i * $this->RESULTS_PER_PAGE;
            $y = $this->RESULTS_PER_PAGE;
            if($pages == $i + 1) $y = $remainder;
            if ($this->debug) print "CventClient::CreateUpdateContacts::Page $i, creating $y Ids starting at array index $x<br/>";
            $batch = array_slice($contacts, $x, $y);        
            $criteria =  NULL;
            $tmp = NULL;
            $criteria->Contacts = $batch;       
            # process batch
            if($type == 'Create') {
                $tmp = $this->client->CreateContact($criteria);
                $tmp = @$tmp->CreateContactResult->CreateContactResult;
            } elseif ($type == 'Update') {
                $tmp = $this->client->UpdateContact($criteria);
                $tmp = @$tmp->UpdateContactResult->UpdateContactResult;
            } else {
                throw new Exception("CventClient::CreateUpdateContacts:: Invalid $type for this method");
            }
            if(isset($tmp)) {
                if(is_array($tmp)) {
                    $result = $tmp;             
                } else {
                    $result = array($tmp);
                }
                if(sizeof($result) != sizeof($batch)) throw new Exception("CventClient::CreateUpdateContacts:: Size of results mismatch with size of batch");
                # organize pass/fails
                $pass = array();
                $fail = array();
                for($j = 0; $j < sizeof($result); $j++) {
                    if(isset($result[$j]->Errors->Error)) {
                        $fail[] = array('contact' => $batch[$j], 'result' => $result[$j]);
                    } else {
                        $pass[] = array('contact' => $batch[$j], 'result' => $result[$j]);
                    }
                }
                $failed = array_merge($failed, $fail);
                $passed = array_merge($passed, $pass);              
            } 
        }
        if(sizeof($passed) + sizeof($failed) != $total) throw new Exception("CventClient::CreateUpdateContacts:: Total pass+fails does not match total contacts.");
        return array('passed' => $passed, 'failed' => $failed); 
    }

    # helpers
    private function RetrieveAllPages($objecttype, $ids) {
        if(!is_array($ids)) $ids = array($ids); // safety measure
        $results = array();
        for($i=0; $i < count($ids); $i += $this->RESULTS_PER_PAGE) {
            if ($this->debug) print "CventClient::RetrievePages:: retrieving $objecttype using Ids from $i to ".($i+$this->RESULTS_PER_PAGE)."<br/>";
            $batch = array_slice($ids, $i, $i + $this->RESULTS_PER_PAGE);
            $criteria = (object) array('ObjectType' => $objecttype, 'Ids' => $batch);
            $tmp = $this->client->Retrieve($criteria);
            if(is_array($tmp->RetrieveResult->CvObject)) {
                $results = array_merge($results, $tmp->RetrieveResult->CvObject);
            } else {
                $results = array_merge($results, array($tmp->RetrieveResult->CvObject));
            }
        }
        return $results;
    }

    public function SearchByFilter($objecttype, $type, $filters)  {
        $response = $this->client->Search((object) array('ObjectType' => $objecttype, 'CvSearchObject' => (object) array('SearchType' => $type, 'Filter' => $filters)));
        return $response;
    }   
}


class SoapDebugClient extends SoapClient {
    public function __doRequest($request, $location, $action, $version, $one_way = 0) {
        //if (DEBUG) print htmlspecialchars($request);
        flush();
        return parent::__doRequest($request, $location, $action, $version);
    }

    public function webServiceDetails() {
        print "<strong>Functions:</strong><pre>";
        print_r($this->client->__getFunctions());
        print "</pre>";

        print "<strong>Types:</strong><pre>";
        print_r($this->client->__getTypes());
        print "</pre>";
    }
    public function debug() {
        print "<pre>";
        print htmlspecialchars($this->client->__getLastRequestHeaders());
        print "</pre>";
        print "<pre>";      
        print htmlspecialchars($this->client->__getLastRequest());
        print "</pre>";
        print "<pre>";
        print htmlspecialchars($this->client->__getLastResponseHeaders());
        print "</pre>";
        print "<pre>";
        print htmlspecialchars($this->client->__getLastResponse());
        print "</pre>";
    }

}
  • Cvent had a lot of updates in the last 4 years, maybe you need an updated class to connect. – Mate Aug 01 '18 at 15:08
  • Sample code for "Logging In" here: https://developers.cvent.com/documentation/soap-api/sample-code/ – Mate Aug 01 '18 at 15:10
  • I've used their documentation to connect with .NET Code no problem. Seems that I only have the issue when trying to write this with PHP. I'm thinking I have something wrong with my code. Unfortunately, Cvent doesn't have any code examples written in PHP. – Nick Rozzi Aug 01 '18 at 15:38
  • Weird... I can connect to Cvent's Sandbox, but not production. I verified that my IP was allowed and that I changed my credentials when moving from Dev to Prod. – Nick Rozzi Aug 01 '18 at 17:21

0 Answers0