Editor’s Note: This blog post comes from Bill Convis of SierraCRM, and talks about how his team worked with our top notch engineering team to find a way to add action menu items across multiple modules. Big thanks to Bill for the writeup, and Peter DeMartini of SierraCRM, and David Wheeler of SugarCRM for helping form the solution.

With the release of SugarCRM 7 came new challenges and opportunities for SugarCRM technology partners. An entirely new paradigm was offered for solution providers to add new features to the power of SugarCRM. Process Manager Enterprise (PM) from SierraCRM, a workflow solution that augments SugarCRM’s existing workflow, was being updated to take advantage of the new architecture.

One of the features to be added to the new release of PM was the ability to see all the processes for a single object. Our intent was to show both processes that had been completed and process that were currently active.

After reviewing the options, a decision was made to add an “action item” to a record view to display a drawer with a listing view of the processes for that record.

We approached John Mertic at SugarCRM and asked him if there was an extensible framework for action buttons, as we wanted to build a solution that was both upgrade safe and would be global across all modules. He suggested we meet with the engineers at SugarCon.

At the SugarCon 2014 conference in San Francisco, Sierra’s senior developer, Peter DeMartini, met up with SugarCRM Engineer David Wheeler at one of the hacker sessions.

David suggested an approach in which we would keep the ./custom/clients/base/views/record/record.php which would define the button for every record. Then a new file that would enable us to include the javascript code behind the button click event in every module in SugarCRM that displays the record view.

This new file would be ./custom/clients/base/layouts/record/record.php.

In record.php we injected our custom layout, pm-record-logic, into the baseline record view. This pm-record-logic layout was the key to ensure that custom javascript code was guaranteed to be present for the modules record view and this javascript would be available on the click event of the new action button.

The final file that was created with the help from David (this was the most important piece for us) was the pm-record-logic.js file. David suggested we extend the initialize function , call the _super method and then bind our code to the click event for the button created above. The code snippet is:

We will now present the code in its entirety with instructions for other solution providers to accomplish the same thing:

The first file is record.php and lives in ./custom/clients/base/views/record/record.php

This file adds the action button to the action menu. The ‘name’ => ‘pm_record_reports’ will map to a file ./custom/clients/base/views/pm-record-logic/pm-record-logic.js with an initialize function that will set this buttons name to a click event function.

The next file is also named record.php and lives in ./custom/clients/base/layouts/record/record.php

What is key in this file is the ‘layout’ => ‘pm-record-logic’. Which allows us to inject our custom view into every record view.

Our first file is:

./custom/clients/base/layouts/pm-record-logic/pm-record-logic.php

And our handle bars file ./custom/clients/base/views/pm-record-logic/pm-record-logic.hbs

And finally our pm-record-logic.js file which makes the api call to Process Manager to retrieve all the processes for the given object:

./custom/clients/base/views/pm-record-logic/pm-record-logic.js

The remaining files used in this customization are the view and layouts for the actual data returned by Process Manager to be shown in the drawer.

What was key to this customization, and what we learned from the outstanding support from John and David, was the ability to be able to guarantee that our new action button click event function in javascript would be placed on the action menu for most modules (excluding Leads) and that we had confidence that the javascript functions on the click event would also be present.

During the Sugar 7 Developer training sessions, the attendees often asked for more code samples to get up to speed writing dashlets. This blog provides a good template that could be used when writing a dashlet that will pull data from an external source.

Quick start
The dashlet is available for free on Github https://github.com/tortugacrm/sugar7_football_wc2014_dashlet
This code is published under the GPL 3 license.
For a quick installation in your Sugar 7 instance, please follow the guidances in §Installation.
The dashlet comes in English, French, German, Swedish and Russian.

Football WC14 dashlet

Introduction
This blog post is a straightforward example how to consume an external service and render the data into a dashlet. The service will be the World Cup in JSON! http://worldcup.sfg.io
This is a free REST API that returns the scores for the games played the current day or the scores for your favorite team.

JSON example:
http://worldcup.sfg.io/matches/today

Remark: if the response is empty, there is no match scheduled for the day. Please try the other call: http://worldcup.sfg.io/matches/country?fifa_code=USA

[{
    "match_number": 45,
    "location": "Arena Pernambuco",
    "datetime": "2014-06-26T13:00:00.000-03:00",
    "status": "in progress",
    "home_team": {
        "country": "USA",
        "code": "USA",
        "goals": 0
    },
    "away_team": {
        "country": "Germany",
        "code": "GER",
        "goals": 0
    },
    "winner": null,
},
ETC...

It looks like another News dashlet? Not at all! If you ever tweak the out of the box News dashlet, you probably noticed that the data source was tough to change. Why? The reason is browser security: a default behavior prevents JavaScript cross-scripting. In short, JavaScript can only call services located on the same server unless they are over https.

Assuming you start with a clone of the News dashlet, the JQuery call to the World Cup service will return a 401 error (unauthorized). How to fix this? All you have is to make the call with a PHP script located on the same web server. The best practice will be extending Sugar REST services by adding a custom endpoint. Then, your JavaScript controller will call the service that will call the external World Cup service and then will receive the data in a JSON format and will send it back to the JS controller.

The code

Edit: 30th June 12:45AM CET. blog & github had been updated. The web service call is made through a custom endpoint.

Now let’s have a look to the code.

The PHP script that calls the service. We implement two calls. The first to retrieve the games for the day, the other one to return all the games for a specific team. If the first call does not return any data, it is because no game is scheduled on that day.

You might already be familiar with the function call_service (it was already published on this blog). I added one parameter at the end that lets the user decide if he wants to receive a JSON string or the data in a structured PHP array.

And a last important remark: the name of the class and the php file has to be the same. I recommend to end this name with “Api”.

./custom/clients/base/api/WorldCup14Api.php

Now the dashlet:
1) The metadata
./worldcup14/worldcup14.php

2) The controller

./worldcup14/worldcup14.js

Easy to understand. the loadData function builds the parameters to provide to the PHP script depending the settings (shall we show today’s matches live scores or my favorite team’s scores?)
Then, this mysterious loop modifies on the fly the date value in order to be shown according to the user’s timezone, based on his settings in Sugar:

$.each(data, function (idx, obj) {
    obj.datetime = formatDateUser(obj.datetime);
});

Note these two functions:
– app.api.buildURL will build the url to call our custom web service.
– app.api.call will call our custom web service.

3) The Handlebar template
./worldcup14/worldcup14.hbs

Remark: as the JSON data do not come with item names, the loop is built on “this” (good to know).

4) The language files
./language.worldcup14/en_us.lang.php

5) And finally the manifest.php
./manifest.php

Installation

This section describes how to install the dashlet. It does not required any technical knowledge. However, please be aware that you will follow the exact steps.

1) Download the module (Zip file) from the Github repository
https://github.com/tortugacrm/sugar7_football_wc2014_dashlet
Click on the ‘Download ZIP‘ button located on the bottom right.
2) Log in your Sugar 7 instance as the admin.
3) Go to the menu located next to the profile picture on the top right. Click on ‘Admin’.
4) Scroll down to the 5th section called ‘Developer Tools’. Click on ’Module Loader’.
5) In the lower section of the page, click on the ‘Choose file’ button, select the wc14.zip archive included in the file you downloaded during step 1. Click on ‘Upload’.
6) Now the module is in the list below. Click on the install button.
7) Accept the license.
8) Wait for the browser to complete the installation. This step might take a couple of minutes, be patient. For some unknown reasons, some instances might finish the installation with a blank screen. It should not, but it’s ok. The installation is finished when your browser stops loading the page.
9) On the top bar, click on ‘Administration
10) Scroll down to the 3rd section called ‘System’. Click on ’Repair’.
11) Click on ‘Quick Repair and Rebuild
12) Wait for the browser to complete the repair. The repair is finished when your browser stops loading the page.
13) Log out
14) Clear your browser cache
15) Log in as Jim
16) Edit the Dashboard
17) Select the area where you want to see you dashlet. Click on the Plus image
18) Chose the ‘Football’ dashlet in the list. You might want to preview it clicking on the preview button (with the eye).
19) Configure the dashlet: chose to view the games for the current day or your fav team’s games
20) Save the dashboard
21) Be a happy user of the Football dashlet!

Acknowledgements
I would like to thank my colleagues: Abhijeet who found out the World Cup Json web site, Harald for reviewing the code, Alena, Anki, Evi for the translations.
Special thank to Jeff Bickart who suggested that I added a custom endpoint in order to provide the best practices in this tutorial.

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’…

There was a discussion recently in Andrea Ayer’s Sugar Adminstration Essentials class about previous layouts are stored in Studio when changing how a screen looks. You can access it on an View edit screen by clicking ‘View History’…

Screen Shot 2014-06-18 at 4.45.54 PM

 

The specific question came up of how many previous layouts are saved. Here’s the answer…

  • By default, the past 50 layouts are saved
  • You can change this by setting the config variable $sugar_config['studio_max_history']
  • Setting $sugar_config['studio_max_history'] to 0 will enable saving all previous layouts

This article is aimed at beginning to intermediate SugarCRM developers who want to customize views in SugarCRM version 7.

This does not go into detail about all the ins and out of creating custom views, changing metadata and handlebars, etc.  This article merely points out a single technique for extending the JavaScript for an out of the box view to a custom view.  This technique also applies to layouts, but this article will concentrate on views.

This article assumes some knowledge of JavaScript and PHP.

Creating a Custom View From an Out of the Box View

When you create a custom view in SugarCRM 7 you create a subdirectory under

{SugarCRM base dir}/custom/clients/base/views/{new view name}

or

{SugarCRM base dir}/custom/modules/{module name}/clients/base/views/{new view name}

with the various files within it named for the view name.  An out of the box view is one you can find under

{SugarCRM base dir}/clients/base/views/

If you wanted to override one of these out of the box views you would simply create a custom view with the same name.  You can tweak the functionality of an out of the box view by simply copying its source files to your custom view and then customizing them from there.  Let’s say, for example, that you wanted to alter the behavior of the “record” view for a given module.  You can simply copy the out of the box record view locally

cd {SugarCRM base dir}
cp -r clients/base/views/record custom/modules/{module name}/clients/base/views/record

You’ll then need to alter the metadata file (in this case record.php) to reflect the new location

<?php
....
$module_name = "my_ModuleName";
$viewdefs[$module_name]['base']['view']['record'] = array(
    'buttons' => array(
    array(
        'type' => 'button',
        'name' => 'cancel_button',
        'label' => 'LBL_CANCEL_BUTTON_LABEL',
        'css_class' => 'btn-invisible btn-link',
    ),
    ...

If your customizations involve JavaScript changes you’ll want to use the un-minified version of the JavaScript file to work against.  You can copy it from the jssource directory.  In this case you’d run the following command:

cp jssource/src_files/clients/base/views/record/record.js custom/modules/{module name}/clients/base/views/record

At this point you can change the various local files to your heart’s content, and anytime the “record” view is used within your module, your custom view will be used instead.

Like all changes to views and layouts, you’ll need to do a Repair and Rebuild before you see your changes take effect.  When you change the JavaScript file you’ll also need to clear your browser cache.

The Problem With Overriding An Out of the Box View

That’s wonderful until you upgrade.  The problem is that when you upgrade, the out of the box record view might receive changes but your local version of the “record” view won’t.  Over time your custom view will become more and more out of step with the out of the box view.  It could even result in your view breaking eventually.  This isn’t much of a problem for the metadata file since that’s not likely to change much from version to version.  The JavaScript file, however, can change a lot.  It would be nice if there were a way to get the benefit of the improved JavaScript while still keeping a customized version of the view.

Happily this is possible by extending the JavaScript file rather than completely overriding it.

To extend the JavaScript file you simply use JavaScript’s “extendsFrom” functionality.  In our example, to extend the record view the record.js file would look something like this:

({

extendsFrom: 'RecordView',

initialize: function(options) {
    console.log("We are using my customized record view, Yay!");
    this._super('initialize', [options]);
}

})

This JavaScript file will work exactly like the original except that it will now write a message to the JavaScript console whenever the view is used.

The tricky part of this is figuring out what to put in the “extendsFrom” part.  The name follows the form {Module}{Layout|View}.  If you’re extending the JavaScript for a view the {Layout|View} part will, of course, be “View”.  For the {Module} part you take the name of the view and capitalize it.  If the name has dashes in it, remove the dashes and uppercase the following word.  For example, if you were extending the JavaScript for the “filter-module-dropdown” view, the module part would turn into “FilterModuleDropdown” and the entire class to extend would become “FilterModuleDropdownView”.

If you’re ever unsure about what the class may be for the view or layout you’re extending, you can run one of the following commands in the JavaScript console and look at the results.  They should contain the names of all the view and layout classes recognized by the application:

SUGAR.App.view.views;
SUGAR.App.view.layouts;

Calling the “_super” method within an overridden method is important to get the functionality of the parent method.  You can leave it out, but then your local method must do everything the parent method did.

This technique also works if you create a custom view of a different name based on an out of the box view.  There’s no rule saying that what follows “extendsFrom” must match the name of the local view.  It’s merely the JavaScript class you’re inheriting from.

Since you inherit from the parent JavaScript class rather than completely overriding it, and use the _super() method within overridden methods, when you update you’ll automatically get the improved functionality of the base class.

A Quick Caveat

When you extend the JavaScript for a view in this manner there’s always the risk that the changes made in a given update will clash with the customizations you’ve made in your local view.  It depends on the nature of the customizations you choose to make.  There’s no good way to completely guard against that other than never touching the Javascript at all.  Extending a view this way does minimize this risk because you override only what you must rather than the entire JavaScript file.  You should, however, remain aware of the customizations you make and test them thoroughly when you update before going live.

I was recently asked how to change the values that get entered into the new record when a user copies an existing record.

Copy action in the Record view dropdown

Copy action in the Record view dropdown

 

This post will discuss how to make a simple change to your module’s record view in order to change how fields get prefilled into a duplicated record in Sugar 7.

Continue Reading…

RESTful Web2Lead Form

Harald Kuske —  April 24, 2014 — 2 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