Archives For Web Services

Important security changes in Sugar 7.8

As we near the release of Sugar 7.8, we wanted to update the Sugar Developer community on a couple of important security changes that are coming in this release.

Continue Reading…

I had this question come from a partner this week…

Users can’t maintain their two sessions of Sugar (One via the REST API, and the other their Sugar Instance) at the same time. Signing in to either one will always result to the session expiration of the other one

This is mostly because the Sugar 7 uses the same REST API to power all the new Sugar UX components, so if you need to let the API know you aren’t coming from the desktop client. The way to do this in as a part of the /oauth2/token call, changing the ‘platform’ parameter as shown below to something other than ‘base’, ‘mobile’, or ‘portal’…

RESTful Web2Lead Form

Harald Kuske —  April 24, 2014 — 6 Comments

If you create a Lead Form in Sugar, this Lead Form uses the entrypoint “WebToLeadCapture” with it’s full qualified web address
e.g. http://myCRMserver/index.php?entryPoint=WebToLeadCapture.

Using this generated form every visitor of your website is informed about the location of your CRM server. If you want to hide the address of the CRM server you should use a Lead Form which uses the web service interface of Sugar in a php script. So the address of your web server is not visible any more in the source code of your website.

The following article describes how to set up such a php page which uses the Lead Form generated in Sugar 7 with some extra code to enter leads via web service in Sugar.

Step 1: Create a “normal” Lead Form in Sugar 7

image04

Drag and drop the fields you want to see in the Lead Form to the two possible columns.

image07

Now configure your form and select the related campaign.

image03

Beautify the form in the html editor…

image00

…and generate the html code.

image01

Step 2: Change the generated html code to a php REST code

Copy the html code of the generated Lead Form into an editor and copy the following lines of code before the first line of the Lead Form code:

<?php
 $rest = "http://myCRMserver/rest/v10"; // your crm server address
 $user = "user"; // userid of the webservice user
 $pass = "pass"; // and the password of user “user”
 $step = $_REQUEST["step"];
 if ($step=="") $step="1";
 if ($step=="1") // show the Lead Form
 {
 ?>

Now search the <form> line:

<form id="WebToLeadForm" action="http://myCRMserver/index.php?entryPoint=WebToLeadCapture" method="POST" name="WebToLeadForm">

and change it to the location of the new php lead form:

<form id="WebToLeadForm" action="http://myWebserver/myWebToLeadCapture.php?step=2" method="POST" name="WebToLeadForm">

http://myWebserver/myWebToLeadCapture.php” is only an example, you can store the new Lead Form in any location on your public web server.

Then add the following code to call the webservice interface at the end of the file:

<?php
}
   else // $step == "2" - generate the Lead
{
   $lead = $_REQUEST;
   $ignore = array("step","redirect_url","req_id"); // fields to be ignored
   foreach ($ignore as $key)
   {
      unset($lead[$key]); // ignore the field
   }
   $url = $rest . "/oauth2/token"; // login
   $oauth2_token_parameters = array(
      "grant_type" => "password",
      "client_id" => "sugar",
      "client_secret" => "",
      "username" => $user,
      "password" => $pass,
      "platform" => "base"
   );
   $oauth2_token_result = call($url, '', 'POST', $oauth2_token_parameters);
   if (empty($oauth2_token_result->error))
   {
      $url = $rest . "/Leads"; // enter Lead
      $post_lead_result = call($url, $oauth2_token_result->access_token, 'POST', $lead);

      $url = $rest . "/oauth2/logout"; // logout
      $oauth2_logout_result = call($url, $oauth2_token_result->access_token, 'POST');
      if (!empty($_REQUEST["redirect_url"])) // redirect
         header("Location: {$_REQUEST['redirect_url']}");
   }
   else
      print("<hr>THAT WAS NOT OK<hr>"); // login not successful
}

and the code for the function “call” which allows easy handling of RESTful webservices to Sugar:

/*******************************************************************************
 * call
 ******************************************************************************/
/**
* Generic function to make cURL request.
* @param $url - The URL route to use.
* @param string $oauthtoken - The oauth token.
* @param string $type - GET, POST, PUT. Defaults to GET.
* @param array $parameters - Endpoint parameters.
* @return mixed
*/
function call($url, $oauthtoken='', $type='GET', $parameters=array())
{
   $type = strtoupper($type);
   $curl_request = curl_init($url);
   if ($type == 'POST')
   {
      curl_setopt($curl_request, CURLOPT_POST, 1);
   }
   elseif ($type == 'PUT')
   {
      curl_setopt($curl_request, CURLOPT_CUSTOMREQUEST, "PUT");
   }
   elseif ($type == 'DELETE')
   {
      curl_setopt($curl_request, CURLOPT_CUSTOMREQUEST, "DELETE");
   }
   curl_setopt($curl_request, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
   curl_setopt($curl_request, CURLOPT_HEADER, false);
   curl_setopt($curl_request, CURLOPT_SSL_VERIFYPEER, 0);
   curl_setopt($curl_request, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($curl_request, CURLOPT_FOLLOWLOCATION, 0);
   if (!empty($oauthtoken))
   {
      $token = array("oauth-token: {$oauthtoken}");
      curl_setopt($curl_request, CURLOPT_HTTPHEADER, $token);
   }
   if (!empty($parameters))
   {
      //encode the parameters as JSON
      $jsonEncodedData = json_encode($parameters);
      curl_setopt($curl_request, CURLOPT_POSTFIELDS, $jsonEncodedData);
   }
   $result = curl_exec($curl_request);
   curl_close($curl_request);
   //decode the response from JSON
   $response = json_decode($result);
   return $response;
}
?>

Step 3: Save the new Lead Form to your webserver location, e.g.http://myWebserver/myWebToLeadCapture.php or any location, as mentioned already above.

There are two additional direct links to your crm server hidden in two JavaScript includes. Check if you need these includes and remove them from the Lead Form or provide the needed JavaScipt functions in your web server environment.

Step 4: Test the new Lead Form

If you call your new Lead Form http://myWebserver/myWebToLeadCapture.php you get the following lead capture screen:

image06

Fill the field values and hit the Submit Button, you will be redirected to the redirect_url.

image05

In your Sugar System you will se the newly generated Lead:

image02

So you’ve seen what the REST API can do and you want more. In this recipe we’ll be covering how to create your own REST endpoint.

Our sample endpoint is going to go beyond the filter API and give us a list of accounts ordered by the number of open cases.

1. Deciding on an endpoint location

This is just about the most important step in adding a new endpoint. The URL of an endpoint should be created using RESTful ideas and placed in the virtual directory tree in a way that will make it easy for developers too see the original intent of this API.

Since this endpoint is just going to be reading data let’s make it a “GET” request.

Because we are dealing primarily with account data we’ll throw it in “/Accounts“.

To finish it off we’ll name the endpoint “at_risk“.

So with it all together our endpoint is “GET /Accounts/at_risk“, now we could have our URL set to anything else but with a descriptive name and using the correct HTTP verb of GET it will help any other developers coming across calls to this endpoint to better understand what we are asking for.

2. Creating the endpoint class

The REST service looks in a lot of locations for endpoint classes:

  • clients/:platform/api/*
  • modules/:module/clients/:platform/api/*
  • custom/clients/:platform/api/*
  • custom/modules/:module/clients/:platform/api/*

Since we are adding a custom accounts endpoint we’ll create a new class “AtRiskApi” and put it in the file “custom/modules/Accounts/clients/base/api/AtRiskApi.php“. It is important to name the file so that it is the same as the class name except with .php at the end otherwise the REST service won’t find our class.

To get this class so it is listening for a specific URL we need to add a function ->registerApiRest(). We are setting the path to array(‘Accounts’, ‘at_risk’) and set the pathVars to array(”, ”) to represent “Accounts/at_risk” and not bring any part of the URL in to our array of arguments. If we wanted to match against a wildcard to look at the at risk profile for a single account record for example we could add a path of array(‘Accounts’, ‘?’, ‘at_risk’) and a pathVars of array(”, ‘id’, ”) which would let us listen for “Accounts/*/at_risk” and would take the second part of the path and populate the id element of our arguments with it.

Next we will actually add a function, setting the method in our register array to getAtRisk lets the REST API know to call that method in this class. We’ll keep this method simple for now and just have it return ‘burgers’ just so we can tell it is working right away. These methods just need to return data and the REST API will take care of all the json encoding for you.

Finally we add a little line in the shortHelp giving a quick description of what this endpoint is for. We’re leaving the longHelp out of this little demo but if you are building endpoints for real be sure to add some detailed documents there.

So, after everything is all said and done, here’s what our little class looks like:

3. Taking it for a test drive

Let’s do a GET request for /rest/v10/Accounts/at_risk

curl -X GET -H OAuth-Token:some-token http://localhost/burgers/rest/v10/Accounts/at_risk

And here is what we get back:

Hey, what gives? First things first let’s check to see if it registered correctly by looking for the endpoint definition in /help, navigate over to /rest/v10/help in your browser and look for it. Not there? didn’t think so.

We added the class and it didn’t load. Since the REST API looks for so many files in so many directories we have to add some heavy duty caching in order to speed up the url lookup on every single request. In order for changes in endpoint definitions to show up we need to login as an admin and run quick repair and rebuild.

After quick repair, let’s check /rest/v10/help again and you should see a line like this:

snapshot1

So let’s try that request again.

curl -X GET -H OAuth-Token:some-token http://localhost/burgers/rest/v10/Accounts/at_risk

Now we get back the correct response:

4. Fetching the data

While having a new URL that says “burgers” is pretty fancy I think we can accomplish more. While there are many ways to fetch and return this data I want to show you the preferred way to do it in Sugar 7.

First things first we need to start off by using SugarQuery. Let’s get a seed bean going by fetching a new bean from the bean factory. We pass that through to the SugarQuery ->from() method to let SugarQuery know we will be querying off of the Accounts module. We’ll limit our result set to just ID’s by adding ->select(‘id’) and then limit our rows to just the first five by adding ->limit(3). From here we can just have it return the results of the ->execute() call and see what that gets us.

Now our getAtRisk function looks like this:

and when we make that same GET request to Accounts/at_risk we get back:

Okay so now we have some simple SQL being run and are returning the result set. How about we add some more complex logic here so we actually fetch the results we want. To start things off let’s join in the cases by adding this “$caseAlias = $q->join(‘cases’)->joinName();“. It’s nice that we just need to use the link field to add a join and everything else is handled by SugarQuery. SugarQuery also understands that we have to go beyond it’s abilities every once in a while, so we need to add a ->fieldRaw() call to fetch the count and then an ->orderByRaw() to properly sort them. We have to use the Raw versions of the functions because neither of those columns are defined in the field_defs for the modules. The ->groupBy() call just needs to group by the account ID so that is simple. Finally the ->where()->notIn() is there so we only fetch related cases that aren’t resolved, no need to quote elements here because SugarQuery will handle that for us.

Added all together it looks like this:

Once again let’s hit Accounts/at_risk and see what we get:

Looking good! Now we are getting the data we need how about we make it look nice for the javascript client that needs it?

5. Formatting the data

To format the data first we have to figure out what to format. Most endpoints accept the argument fields to specify which fields they want returned from the api and we’ll keep up that tradition here with some simple parsing of the $args array.

Next up we want to convert the results of the query into beans so they can be properly formatted. Previously you would have to perform the PHP equivalent of banging two rocks together to make fire by manually fetching the rows and creating beans and populating them via ->populateFromRow(). Fortunately we are introducing a helper function in SugarBean named ->fetchFromQuery() to help automate and centralize this process, so we’ll just call that here. We need to pass ->fetchFromQuery() the option of returnRawRows because we need to populate the case_count field manually because it doesn’t exist in any of the field definitions.

With the beans returned from ->fetchFromQuery() we strip out the raw rows from the result set and then pass the remaining beans through to ->formatBeans() so that our returned results look the same as every single other API call. After we get the results back as an array from ->formatBeans() we loop through the results and jam the case_count in there.

So with all that, here’s what our final method looks like:

And when we finally call the Accounts/at_risk, here is what we get:

curl -X GET -H OAuth-Token:some-token http://localhost/burgers/rest/v10/Accounts/at_risk?fields=id,name,date_modified

6. All done!

That’s all, I hope this clears how to add your own endpoint to Sugar 7. Along with some helpful tips on how to use a combination of SugarQuery, ->fetchFromQuery() and ->formatBeans() to create easy and standardized code for returning data from the API. Add a comment if you have questions.

A while back we touched on exporting a report as a PDF using the “new” RESTful API. One thing the reporting tool enables you to do is export ‘Rows and Columns’ reports as CSV files, so naturally being able to do this via the RESTful API would be great as well. But alas, this isn’t possible to do out of the box. But since the API is easily extensible, you can add this in with ease.

To get started, drop the below contents into a new file ReportsExportCsvApi.php in the custom/modules/Reports/clients/base/api/ directory…

Now to get this, just call the endpoint /Reports/<<report id>>/csv, and you’ll get back the CSV formatted file. Note this will only work for ‘Rows and Columns’ reports, as this is the only type where exports work for in the OOTB product.

I was excited to learn today from Aron Hoekstra that their Starfish ETL tool has successfully transitioned to using the new REST API in Sugar 7, which has given them a huge speed and usability improvement. From the announcement…

Get a jump start on your next SugarCRM data integration or migration project using Starfish ETL 3.1. Ours is the first (and only) ETL tool on the market with a connector to SugarCRM’s newly revamped REST API. It’s faster, easier to use and more powerful than the previous SOAP-based API, making it a great improvement for working with data imports & exports. You’ll need SugarCRM 7.0 (in beta as of writing this). Full support for oauth2 login, all modules including custom, setting links/relationships. 

You can give the new connector a test drive now. Looking forward to more apps beginning to leverage the new API!

I was excited to learn today from Aron Hoekstra that their Starfish ETL tool has successfully transitioned to using the new REST API in Sugar 7, which has given them a huge speed and usability improvement. From the announcement…

Get a jump start on your next SugarCRM data integration or migration project using Starfish ETL 3.1. Ours is the first (and only) ETL tool on the market with a connector to SugarCRM’s newly revamped REST API. It’s faster, easier to use and more powerful than the previous SOAP-based API, making it a great improvement for working with data imports & exports. You’ll need SugarCRM 7.0 (in beta as of writing this). Full support for oauth2 login, all modules including custom, setting links/relationships. 

You can give the new connector a test drive now. Looking forward to more apps beginning to leverage the new API!