What does it take to get a SOAP server setup? What's needed architecturally to configure the end point? Just any PHP server that is ready and able to connect to Salesforce using the PHP toolkit?
Recently I have been using the Salesforce outbound SOAP messages while I was working on some workflow's. Honestly I had no idea that this could be implemented and I am in awe right now.
My organization needed to find a way to change the Owner Id on a custom object when a certain field changed. We implemented a workflow, which in turn would thin trigger an outbound message from Salesforce via the API. An outbound message looks like and is formatted in XML. Here is an example:
The key to this is that you are allowed to include almost any field in the outbound message. You can set these values when you are creating the trigger in Salesforce.Code:<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <notifications xmlns="http://soap.sforce.com/2005/09/outbound"> <OrganizationId>00DR......</OrganizationId> <ActionId>04kR00000004CCXIA2</ActionId> <SessionId xsi:nil="true"/> <EnterpriseUrl>https://cs2-api.salesforce.com/services/Soap/c/8.0/4e1300DR00000000XcD</EnterpriseUrl> <PartnerUrl>https://cs2-api.salesforce.com/services/Soap/u/8.0/4e1300DR00000000XcD</PartnerUrl> <Notification> <Id>04lR0000000CcIFIA0</Id> <sObject xsi:type="sf:Design_Registration__c" xmlns:sf="urn:sobject.enterprise.soap.sforce.com"> <sf:Id>a06R00000009QZaIAM</sf:Id> <sf:BU_Reviewer_Lookup__c>00540000000lnc2AAA</sf:BU_Reviewer_Lookup__c> <sf:DP_cust_eng_email__c>me@email.com</sf:DP_cust_eng_email__c> <sf:DP_reg_id__c>JXD08-7CLLB7</sf:DP_reg_id__c> <sf:DR_Number__c>R080500453</sf:DR_Number__c> <sf:Field_reviewer_lookup__c>00540000000yHbGAAU</sf:Field_reviewer_lookup__c> <sf:Name>URT</sf:Name> <sf:OwnerId>00540000000mBqsAAE</sf:OwnerId> </sObject> </Notification> </notifications> </soapenv:Body> </soapenv:Envelope>
Once you set the fields, they are sent in the sObject portion of the SOAP message:
The great thing is that you can then kick off almost any application with this with a SOAP Server that is setup locally (we have one running on our server).Code:<sObject xsi:type="sf:Design_Registration__c" xmlns:sf="urn:sobject.enterprise.soap.sforce.com"> <sf:Id>a06R00000009QZaIAM</sf:Id> <sf:BU_Reviewer_Lookup__c>00540000000lnc2AAA</sf:BU_Reviewer_Lookup__c> <sf:DP_cust_eng_email__c>me@email.com</sf:DP_cust_eng_email__c> <sf:DP_reg_id__c>JXD08-7CLLB7</sf:DP_reg_id__c> <sf:DR_Number__c>R080500453</sf:DR_Number__c> <sf:Field_reviewer_lookup__c>00540000000yHbGAAU</sf:Field_reviewer_lookup__c> <sf:Name>URT</sf:Name> <sf:OwnerId>00540000000mBqsAAE</sf:OwnerId> </sObject>
The key is that you have to send a response back to your instance of Salesforce.
So once the SOAP Server is setup on your server, you can respond to an incoming message like this:
This code reads the raw data coming in from Salesforce and then sticks it in a variable called $content.PHP Code://Reads SOAP incoming message from Salesforce/MAPS
$data = fopen('php://input','rb');
$content = fread($data,5000);
You can then respond to the message like this:
So $tf is set to true and responds to the outbound message and Salesforce knows that the message has been accepted and is removed from the queue.PHP Code:if ($content)
{
respond('true');
}
else
{
respond('false');
}
function respond($tf)
{
print '<?xml version = "1.0" encoding = "utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<Ack>' . $tf . '</Ack>
</notifications>
</soapenv:Body>
</soapenv:Envelope>';
}
So what can you really do with this? We are using this workflow to change the OwnerId on a custom object when a certain field is changed
I will continue to update this thread with different topics and ideas on what I have been able to accomplish with this on demand feature
~Mike
What does it take to get a SOAP server setup? What's needed architecturally to configure the end point? Just any PHP server that is ready and able to connect to Salesforce using the PHP toolkit?
Hey Scott !!
listen the setup (still totally new at this) was rather quite easy
First off, do you know how to setup the outbound workflow in Salesforce?
I am sure you do, you are a lot more knowledgeable than I am, LOL!
Listen there is no special setup for the SOAP server setup.
I just have that code that I posted in the first post of this thread and a parser to read XML (the incoming SOAP message)
for testing purposes, I wrote it out to a file and then used that to parse the XML until I felt comfortable with the data I was seeing using print_r.
You can use PHP's built in XML DOM. I found this example at the salesforce forums:
to get me started. I had some small troubles and also purchased a off the shelf XML parser that reads the SOAP message better than the DOM XML built in class.PHP Code:<?php
/* Handles SFDC Outbound Message notification.*/
require_once ('../includes/salesforce_login.php');
// Get raw post data
$data = fopen('php://input','rb');
$content = fread($data,5000);
// Parse values from soap string
$dom = new DOMDocument();
$dom->loadXML($content);
$resultArray = parseNotification($dom);
$sObject = $resultArray["sObject"];
// Output field names and values to file
$fields_string = "";
foreach ($sObject->fieldnames as $field)
{
$fields_string .= $field . " => " . $sObject->fields->$field . "\n";
}
// Write message and values to a file
$fh = fopen('soap.xml','a');
fwrite($fh,$content);
$ret = fclose($fh);
// Sends SOAP response to SFDC
if ($ret)
{
respond('true');
}
else
{
respond('false');
}
//functions
/* Parse a Salesforce.com Outbound Message notification SOAP packet
* into an array of notification parms and an sObject. */
function parseNotification($domDoc)
{
// Parse Notification parameters into result array
$result = array("OrganizationId" => "","ActionId" => "","SessionId" => "","EnterpriseUrl" => "","PartnerUrl" => "","sObject" => null);
$result["OrganizationId"] = $domDoc->getElementsByTagName("OrganizationId")->item(0)->textContent;
$result["ActionId"] = $domDoc->getElementsByTagName("ActionId")->item(0)->textContent;
$result["SessionId"] = $domDoc->getElementsByTagName("SessionId")->item(0)->textContent;
$result["EnterpriseUrl"] = $domDoc->getElementsByTagName("EnterpriseUrl")->item(0)->textContent;
$result["PartnerUrl"] = $domDoc->getElementsByTagName("PartnerUrl")->item(0)->textContent;
// Create sObject and fill fields provided in notification
$sObjectNode = $domDoc->getElementsByTagName("sObject")->item(0);
$sObjType = $sObjectNode->getAttribute("type");
if (substr_count($sObjType,"sf:"))
$sObjType = substr($sObjType,3);
$result["sObject"] = new SObject($sObjType);
$result["sObject"]->type = $sObjType;
$sObjectNodes = $domDoc->getElementsByTagNameNS('urn:sobject.enterprise.soap.sforce.com','*');
$result["sObject"]->fieldnames = array();
foreach ($sObjectNodes as $node)
{
if ($node->localName == "Id")
{ // Id goes under sObject->Id
$result["sObject"]->Id = $node->textContent;
}
else
{ // ... the rest go under sObject->fields
$fieldname = $node->localName;
$result["sObject"]->fields->$fieldname = $node->nodeValue;
array_push($result["sObject"]->fieldnames,$fieldname);
}
}
return $result;
} // end parseNotification
function respond($tf)
{
print '<?xml version = "1.0" encoding = "utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<Ack>' . $tf . '</Ack>
</notifications>
</soapenv:Body>
</soapenv:Envelope>';
}
?>
So basically it is easy to setup and get working, I can take this offline sometime if you want
~Mike
PS: good to see you here, I need to add a link to your site on my main page, I forgot to do that
Awesome. Got it working! Now I need to figure out what to implement, but this is a really nice option to have.
Outstanding man!! Glad that helped.
Listen if you come up with Ideas, fixes or things that work and you post them on your site, would you be willing to post a link here or the code and let me know what you did or found?
FYI > I added a link from my main menu to your site
~Mike
Scott we have taken this service and added a whole new object via the outbound messaging. It is really like combining a whole bunch of queries using the data coming in from the salesforce outbound messaging and querying multiple tables
Then we take that data and create a whole new data flow for a new Object in Salesforce. It works like a charm
So if they change a status on the custom object, it kicks off a SOAP outbound message that you could really do anything with!!
Stick it into a custom reporting application, A database, or back into Salesforce
I am thinking this is like the new trigger function in APEX, but keeping your process outside of Salesforce and not having to learn APEX coding (even though I need too)
Have you had a chance to mess around with this at all
~Mike
One item that I did not take into consideration was error reporting and what it can cause in your responses to salesforce's outbound messaging queue
Let me explain
Last week I was running into errors when sending 10 fields in an outbound message and forgot to account for empty fields in the data elements.
So let's say you send the following from the Account Object:
* FirstName
* LastName
* Name
* UserName
* Phone
* Mobile
And in some cases not all the data is populated, well if your error_reporting is turned on and you do not set it correctly to ignore notice's, you can run into some serious problems trying to get the outbound message queue from clearing out.
I was sending notices back to Salesforce with the True response and noticing that the message queue was filled up with many requests.
A fellow developer at work told me to check what was in the output buffer, basically what I was sending back to salesforce
I had no idea that the notices were being sent back, but once I added the mail() function to email myself the ob_get_contents(), I saw the other data that I was returning to Salesforce, what a mess that was!
a simple line at the top of the script to turn off notice reporting was all that I needed
Stupid mistake, but I thought that I would share it with the community because you could run into the same problemsPHP Code:error_reporting(E_ALL ^ E_NOTICE);
Anyway just a little snippet to take care of clearing out the buffer before sending your response to Salesforce
This thread has been my primary resource for outbound messaging via SalesForce. Thanks for all the great input.
My client has 2 primary means of accessing their SalesForce data:
1. Through the web at login.salesforce.com, na2.salesforce.com, etc. using SF's snazzy Ajax interface.
2. Through a custom CMS web app designed to talk to salesforce through the web service API.
My question is: apart from having 2 different logins, how do I tell the 2 kinds of requests apart?
Let's take this scenario: I modify the Contact object through the CMS (case 2 above). This forces the CMS to push the changes to the salesforce database. I have an outbound message being triggered for all changes to a Contact object. The outbound message gets triggered and notifies the CMS that the Contact object has changed, even though the CMS initiated the change in the first place.
How do I keep salesforce from generating the outbound message when it was triggered by an event via a webservice API call?
Thanks in advance,
/a
When you create an outbound message, you can set who you want to send the outbound message as, a user id, like me (see the attached picture)
Maybe you could use 2 different users to send as, thus two different identifications that you can use to differentiate the two outbound messages.
I did not know that you could kick off a outbound message via an API call, but you can limit it to only being called once!
when you set the Work Flow rule, there is a section:
Evaluate rule How do I choose?
Hope that helps sir and I hope that I understood your question(s)
- When a record is created, or when a record is edited and did not previously meet the rule criteria (choose this one)
- Only when a record is created
- Every time a record is created or edited
If Not then do let me know
Yesterday I came across a major problem with one of our outbound soap processes that I would like to bring up.
I knew that Salesforce assigned a processing Id with their outbound message, which makes since. I always thought that this was a 1 for 1 relationship, but I am totally mistaken.
We had a discrepancy in some of our counts in two different objects and could not figure it out. Further testing revealed that Salesforce can send multiple records with one processing Id. Look at the XML code below from one processing Id:
I wrote the script on the basis of the 1 to 1 relationship and hence, it could not. I will rewrite it on Monday morning so it can iterate through multiple line items and process all the SOAP inbound messages. This will allow for mutliple records to processCode:<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <notifications xmlns="http://soap.sforce.com/2005/09/outbound"> <OrganizationId>*****************</OrganizationId> <ActionId>04k400000004CYMAA2</ActionId> <SessionId xsi:nil="true"/> <EnterpriseUrl>https://cs2-api.salesforce.com/services/Soap/c/8.0/</EnterpriseUrl> <PartnerUrl>https://cs2-api.salesforce.com/services/Soap/u/8.0/</PartnerUrl> <Notification> <Id>04lR00000004z4uIAA</Id> <sObject xsi:type="sf:OpportunityLineItem" xmlns:sf="urn:sobject.enterprise.soap.sforce.com"> <sf:Id>00k40000004PHXtAAO</sf:Id> </sObject> </Notification> <Notification> <Id>04lR00000004z4qIAA</Id> <sObject xsi:type="sf:OpportunityLineItem" xmlns:sf="urn:sobject.enterprise.soap.sforce.com"> <sf:Id>00k40000004P5IhAAK</sf:Id> </sObject> </Notification> <Notification> <Id>04lR00000004z4vIAA</Id> <sObject xsi:type="sf:OpportunityLineItem" xmlns:sf="urn:sobject.enterprise.soap.sforce.com"> <sf:Id>00k40000004PK51AAG</sf:Id> </sObject> </Notification> </soapenv:Body> </soapenv:Envelope>
Just be aware that there is a 1 to many relationship when it comes to SOAP messages from Salesforce and adjust your processes accordingly.
I will keep this thread updated with the changes and let you know how it goes
~Mike
Bookmarks