Thursday, December 13, 2012

Starting with the Sugar API


Introduction

The Sugar API is easy to use and powerful. With the API, Sugar can talk to any external systems: BI systems, ERP, ETL, ESB, custom applications, etc.
I wrote this post in order to help people who need a quick ramp up in writing custom code.

This blog will show how to retrieve records based on:
1- a record id
2- a filter on a module
3- a sql query on a module
4- a report


Quick takeaway

The Sugar API let you make awesome queries that you cannot do with other CRM systems. 


Before we start

I recommended to check my 2 others posts related to this topic:
1- SugarCRM: SOAP and REST calls using PHP
2- SugarCRM+Salesforce: Live data synchronization between Salesforce.com and SugarCRM



LOG IN YOUR INSTANCE

Today I will show you how to retrieve records using the API. I wrote the code samples in PHP, you may run them from the command line or add them to a web application.

We are working with REST web services. First of all, you need to log in with your credentials. The Sugar API does not need your password. You will use a md5 transformation. To get your md5 password, run from the command line:
php -r "echo md5('yourPassword').\"\\n\";"

Here the code to log in your instance. We will start all the PHP codes with this bloc.

Define the verbose level (none, debug, info):
use the || boolean operator to combine several log levels. This log level is define in my code and is not related to PHP or to the Sugar framework.
define('NONE'0);
define('DBG'1);
define('IFO'2);
//$dg = IFO || DBG;
$dg IFO;
In this example my server name is sugarcrm.ubuntu1.

I am calling Sugar API version 4_1 (read "What Version of the API Should I Be Using?" to get your flavor) //
// LOGIN
//
$url 'http://sugarcrm.ubuntu1/service/v4_1/rest.php';
$curl curl_init($url);
curl_setopt($curlCURLOPT_POSTtrue);
curl_setopt($curlCURLOPT_HEADERfalse);
curl_setopt($curlCURLOPT_RETURNTRANSFERtrue);
$parameters =
  array(
    
"user_auth" =>
        array(
        
'user_name' => "admin",
        
'password' => "0192023a7bbd73220516f0ffdf18b532",
        
'version' => "0.1"
        
),
    
"application_name" => ''
  
);
$json json_encode($parameters);
$postArgs 'method=login&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if(!
is_object($result)) { die("Connection error\n"); }


Check if the login succeeded
if ($dg&DBG) { print_r($result); echo "\n"; }
if (isset(
$result->number)) { // Invalid Login?
    
echo $result->name "\n";
    exit;
}


Get the session id
$sessionid $result->id;
$userid=$result->name_value_list->user_id->value;
if (
$dg&DBG) echo "sessionid=$sessionid\n";
if (
$dg&DBG) echo "userid=$userid\n";

EXAMPLE 1: retrieve a record based on its ID

Copy/paste the log in code. We need the $sessionid value to make the core call.
We are making the API call get_entry. We already know the record ID. It is an account. We want the API to send back the account name and the shipping city.
//
// Get 1 account (540c66a3-de28-790b-e3b9-4fc9c84d6880)
//
// get_entry(session, module_name, id,select_fields, link_name_to_fields_array)
$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'id' => '540c66a3-de28-790b-e3b9-4fc9c84d6880',
    
'select_fields' => array('name','shipping_address_city')
);
$json json_encode($parameters);
$postArgs 'method=get_entry&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
if (
$dg&IFO) echo "response json=$response\n";
$result json_decode($response);
if (
$dg&IFOprint_r($result);
curl_close($curl); 
Print out the result in CSV style:  

echo "id;name;city\n";
foreach (
$result->entry_list as $i) {
    echo 
$i->id.";";
    echo 
$i->name_value_list->name->value.";";
    echo 
$i->name_value_list->shipping_address_city->value."\n";
}


Sample output:

id;name;city
540c66a3-de28-790b-e3b9-4fc9c84d6880;SugarCRM Inc.;Cupertino


What we are sending & receiving? This serialized data, based on Json.
Sugar's response in Json is:
{"entry_list":[{"id":"540c66a3-de28-790b-e3b9-4fc9c84d6880","module_name":"Accounts","name_value_list":{"name":{"name":"name","value":"SugarCRM Inc."},"shipping_address_city":{"name":"shipping_address_city","value":"Cupertino"}}}],"relationship_list":[]}

In a human readable form (using print_r):

stdClass Object
(
    [entry_list] => Array
        (
            [0] => stdClass Object
                (
                    [id] => 540c66a3-de28-790b-e3b9-4fc9c84d6880
                    [module_name] => Accounts
                    [name_value_list] => stdClass Object
                        (
                            [name] => stdClass Object
                                (
                                    [name] => name
                                    [value] => SugarCRM Inc.
                                )

                            [shipping_address_city] => stdClass Object
                                (
                                    [name] => shipping_address_city
                                    [value] => Cupertino
                                )

                        )

                )

        )

    [relationship_list] => Array
        (
        )

)


the property entry_list in an array. We are looping on it. To get the account name, the PHP code is: 
$i->name_value_list->name->value



EXAMPLE 2: retrieve records in a module, based on a filter

We want to select all the accounts where the name starts with the letter 'S'.
The SQL query is: 
select id,name from accounts where name like 's%';
We use the core call get_entry_list
Remark: as Sugar is making a soft delete of the records, we need to specify that we are filtering on the existing records with:
 'deleted' => '0'
Here is the code, straightforward:

//
// Get accounts "select id,name from accounts where name like 's%'"
//
//get_entry_list(session, module_name, query, $order_by,offset, select_fields,
//               link_name_to_fields_array, max_results, deleted)

$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'query' => "name like 's%'",
    
'name',
    
'offset' => 0,
    
'select_fields' => array('id''name'),
    
'link_name_to_fields_array' => array(),
    
'max_results' => '100',
    
'deleted' => '0'
);
$json json_encode($parameters);
$postArgs 'method=get_entry_list&input_type=json&response_type=json&rest_data=' $json;
echo 
"postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if (
$dg<=IFOprint_r($result);
curl_close($curl); 

echo 
"id;name\n";
foreach (
$result->entry_list as $i) {
    echo 
$i->id.";";
    echo 
$i->name_value_list->name->value."\n";
}



EXAMPLE 3: retrieve records using a SQL query on a module

We are synchronizing with an external system and need to know which accounts had been modified in the past 3 days. The SQL query is:
select id,name from accounts where date_modified > DATE_ADD(NOW(), INTERVAL -3 DAY);
We do not need to handle date format or date construction, the database is making the job. We are using the core call get_entry_list.

Here is the code:

//
// Get accounts "select id,name from accounts where date_modified > DATE_ADD(NOW(), INTERVAL -3 DAY)
//
//get_entry_list(session, module_name, query, $order_by,offset, select_fields,
//               link_name_to_fields_array, max_results, deleted)

$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'query' => "date_modified > DATE_ADD(NOW(), INTERVAL -3 DAY)",
    
'name',
    
'offset' => 0,
    
'select_fields' => array('id''name''date_modified'),
    
'link_name_to_fields_array' => array(),
    
'max_results' => '100',
    
'deleted' => '0'
);
$json json_encode($parameters);
$postArgs 'method=get_entry_list&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if (
$dg&IFOprint_r($result);
curl_close($curl); 

echo 
"id;name\n";
foreach (
$result->entry_list as $i) {
    echo 
$i->id.";";
    echo 
$i->name_value_list->name->value.";";
    echo 
$i->name_value_list->date_modified->value."\n";
}




EXAMPLE 4: retrieve records using a report

You need a commercial edition of Sugar.
We want to run a report using the API. The report is a standard report called "All Open Opportunities". We are using the core call get_report_entries and provide the report ID and the fields.


//get_report_entries(session,ids,select_fields)
// report Id=3be50900-e1bb-2da8-be27-4fc769f9f8c1, Name="All Open Opportunities"

$parameters = array(
    
'session' => $sessionid,
    
'id' => array('3be50900-e1bb-2da8-be27-4fc769f9f8c1'),
    
'select_fields' => array('id''name''date_modified'),
);
$json json_encode($parameters);
$postArgs 'method=get_report_entries&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if (
$dg&IFOprint_r($result);
curl_close($curl); 

foreach (
$result->field_list[0] as $i)
    echo 
$i->label.";";
echo 
"\n";

foreach (
$result->entry_list[0] as $record) {
    if (!isset(
$record->name_value_list)) continue;
    foreach (
$record->name_value_list as $i) echo $i->value.";";
    echo 
"\n";
}



The output in CSV style:

Opportunity Name;Type;Sales Stage;Expected Close Date;Amount;User Name;
Calm Sailing Inc - 1000 units;New Business;Qualification;2012-06-01;$75,000.00;will;
Anytime Air Support Inc - 1000 units;Existing Business;Id. Decision Makers;2012-06-11;$25,000.00;max;
Income Free Investing LP - 1000 units;Existing Business;Id. Decision Makers;2012-06-25;$25,000.00;chris;
Airline Maintenance Co - 1000 units;New Business;Perception Analysis;2012-06-29;$75,000.00;sarah;
etc.



Have fun with the Sugar API !

Monday, December 10, 2012

Quick tips for a successful deployment & user adoption: duplicate the dashboards


Introduction

User adoption is the key to a successful CRM project. Sugar will help you to accomplish this goal: Sugar flexibility and intuitiveness will let the end user feel like home. He may set up his CRM to look the way he wants: setting his home page drag & dropping dashlets, adding graphic reports, adding tabs has never been so easy.

But if you are working on a big deployment, you need to automatize tasks. However, some of them are not (yet) available out of the box in Sugar (and will never exist in Salesforce, he he): set the user password to a value and duplicate the dashboards. Both will be done programmatically. This is straightforward, no sweat.

Why setting a password? 
Sugar can generate a temporary password. The idea behind this is to leverage user adoption by setting human readable password that you will choose. Or it could for any other reasons:  e.g. setting the same password in different systems (you should then better consider Single Sign-On), setting an initial password that will be compliant with your corporate password rules.

Why setting dashboards?
For example you are launching a new CRM system for a call center and you want all the Support Reps to start with the same dashboard. And you might want to choose to freeze the dashboards and prevent the users from modifying them.

Assumption
You can access to your database with a client (gui or the command line).


Setting the password
The password is stored as a hash value in the database. The function that is calculating it is located on the User object. I highly recommended you to use the Sugar framework in order to stay upgrade safe.

Write a PHP file starting from the example below, save it to your Sugar instance root directory, log in the instance and execute the PHP file with your browser.


<html>
<head></head>
<body>
<?php
include ('include/MVC/preDispatch.php');
$startTime microtime(true);
require_once(
'include/entryPoint.php');
ob_start();
require_once(
'include/MVC/SugarApplication.php');
$app = new SugarApplication();
$app->startSession();

$u = new User();
$u->retrieve('seed_sally_id');
$u->user_hash User::getPasswordHash('sally');
$u->save();

echo 
"Password changed for Sally!";
?>
</body>
</html>


Alternative solution
Since you got the hash value using PHP, you may shoot a sql query:
update users
set user_hash='$1$rINuoIur$DhLu0b2AUgjrll7jX5bPP.' 
where user_name='sally';


Duplicate the dashboards
The dashboards user preferences are stored in the database, table user_preferences.
Before making changes, let's do a quick backup:
mysqldump --add-drop-database -u userdb -p'password' dbinstancename user_preferences > user_preferences-backup.sql

Then, create a new user, give him the expected role and set his dashboard. You might want to copy the preferences from an existing user, but this user should not use Sugar during the copy process.
When duplicating the user preferences, you will copy all the customizations: dashboards, list views, sub panels orders, etc.

Duplicating his preferences is easy, just 1 sql line:
insert into user_preferences(id,assigned_user_id,contents,category) 
select UUID(),'target user id',contents,category from user_preferences where assigned_user_id='source user id';

When duplicating the user preferences, you will copy all the customizations: dashboards, list views, sub panels orders, etc. To reduce the scope, add a "where clause":
insert into user_preferences(id,assigned_user_id,contents,category) 
select UUID(),'target user id',contents,category 
from user_preferences 
where assigned_user_id='source user id' and category like 'Home%';

A best practice would be to set the dashboard on a user and copy his preferences to a dedicated table. Let's create this table from the user_preferences:
CREATE TABLE `user_preferences_cp` (
  `id` char(36) NOT NULL,
  `category` varchar(50) DEFAULT NULL,
  `deleted` tinyint(1) DEFAULT '0',
  `date_entered` datetime DEFAULT NULL,
  `date_modified` datetime DEFAULT NULL,
  `assigned_user_id` char(36) DEFAULT NULL,
  `contents` longtext,
  PRIMARY KEY (`id`),
  KEY `idx_userprefnamecat_cp` (`assigned_user_id`,`category`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Copy the preferences to this table:
insert into user_preferences_cp(id,assigned_user_id,contents,category) 
select UUID(),'source user id',contents,category from user_preferences where assigned_user_id='source user id';

Then, when duplicating, use the new table:
insert into user_preferences(id,assigned_user_id,contents,category) 
select UUID(),'target user id',contents,category from user_preferences_cp where assigned_user_id='source user id';

Enjoy your new dashboards :-)

Wednesday, October 10, 2012

Add a Twitter flavor to your Sugar!

Following my presentation "Sugar + SocialCRM" during the CRM Acceleration in Paris last month, I presented quick & smart ways to bring cool Social interaction to Sugar. Sugar 7 will come with great features as well, but why waiting for April 2013. Start today!

In this post I will address how to quickly add a Twitter Feed based on the Twitter name of an Account or a Contact and a Twitter Search. In future posts, I will present the Facepile and the Twitterpile. Then the Klout integration, and the Google Map mashup.

The expected result will be getting an iFrame on the account page showing the Twitter identity of the Account and the last Tweets he posted:

And also a Twitter search based on the Account Twitter Name:

Let's start with a simple page!

We are working with the Account but the same behavior can easily be added to the Contact.

Step 1 
In the Sugar Studio, add a text field called twittername. This is basically the Account Twitter "screen name". Add the field to the page layout. Then I am assuming that someone in your company will fill the value for the account. How to find it? Just make a Twitter search with the account name, I guarantee you that you will find immediately the company Twitter account if they have one.

Step 2
Download JQuery.
In your Sugar root directory, create those directories 
/social/js/jquery/1.8.1
/social/twitter/minitwitter
Adapt the jquery version number to the version you have downloaded.

Step 3
I chose a JavaScript library called MiniTwitter (relying on JQuery):
Why JavaScript? Because this is the fastest way to retrieve the data from Twitter. The web server is delivering to your browser the page content, then your browser is taking care of the dialog with Twitter. The calls are asynchronous, so it will not slow down the rendering of the page.
MiniTwitter is very easy to use. See the examples on their front page. 

Wanna show the last Tweets from Lady Gaga on your blog? Ok, I know that you do not want, nobody wants it! Stop whining, let's choose a better example and get the lastest tweets from Simone Simons, the lead singer of the Dutch band Epica :)

JavaScript code:
$('.content_tweets').miniTwitter('SimoneSimons');


HTML:
<div class="content_tweets"> </div>

See the last 5 Tweets from Simone Simons:
Mini Tweets
Remark:
There is a fix to be made in MiniTwitter, file jquery.minitwitter.js:
line 132
obj.avatar = '<div class="tweet"><div class="avatar"><a '+rel()+' '+target()+' class="mt_avatar" href="'+obj.userUrl+'"><img src="'+obj.image+'" alt="'+obj.realName+'\'s avatar" border="0"/></a></div>';
 
add the width and height parameters to avoid surprises; Twitter might show very big avatars pics, you never know, so it is good to force the pic size.
obj.avatar = '<div class="tweet"><div class="avatar"><a '+rel()+' '+target()+' class="mt_avatar" href="'+obj.userUrl+'"><img src="'+obj.image+'" alt="'+obj.realName+'\'s avatar" border="0" width="48" height="48" /></a></div>';

Step 4
Let's create a very simple PHP page getTweets-simple.php to show the last tweet from any Twitter account:
<html>
<head>
<script language="javascript" src="/social/js/jquery/1.8.1/jquery.min.js" type="text/javascript"></script>
<script language="javascript" src="/social/twitter/minitwitter/jquery.minitwitter.js" type="text/javascript"></script>
<link href="/social/twitter/minitwitter/jquery.minitwitter.css" media="all" rel="stylesheet" type="text/css"/> 
<link rel="stylesheet" type="text/css" href="/social/social.css" />
<style type="text/css" media="screen">
  #custom-tweet-button a {
    display: block;
    padding: 2px 5px 2px 20px;
    background: url('https://twitter.com/favicons/favicon.ico') 1px center no-repeat;
    border: 1px solid #ccc;
    max-width:500px;
  }
</style>
</head>
<body>
<?php
$twitterName 
= (isset($_GET['twitterName']))?$_GET['twitterName']:'';
if (
$twitterName=='') {
    echo 
'<h2>Please provide a Twitter Name</h2>';
    echo 
'</body></html>';
    exit;
}
?><div class="tweets"> <div class="tweets_header">Mini <a href="http://minitwitter.webdevdesigner.com">Tweets</a></div> <div class="content_tweets"> </div> <div class="tweets_footer"> <a href="#"><span id="bird"></span></a> </div> </div>
<script type='text/javascript'>
jQuery(".content_tweets").miniTwitter("<?php echo $twitterName ?>");
</script>
</body>
</html>

Call the page: http://yourserver.com/social/getTweets-simple.php?twitterName=sugarcrm

Now let's write a get Tweets / search page
The page will take 2 parameters: TwitterName for retrieving Tweets from an account or search for making a search.
Let's add a cool box to enable the user to write a Tweet to the Twitter account or a new Tweet containing the search pattern as a hashtag.
And let's add a nice box at the top on the page to get all the basic information on the Twitter account.

PHP page getTweets.php:
<html>
<head>
<script language="javascript" src="/social/js/jquery/1.8.1/jquery.min.js" type="text/javascript"></script>
<script language="javascript" src="/social/twitter/minitwitter/jquery.minitwitter.js" type="text/javascript"></script>
<link href="/social/twitter/minitwitter/jquery.minitwitter.css" media="all" rel="stylesheet" type="text/css"/> 
<link rel="stylesheet" type="text/css" href="/social/social.css" />
<style type="text/css" media="screen">
  #custom-tweet-button a {
    display: block;
    padding: 2px 5px 2px 20px;
    background: url('https://twitter.com/favicons/favicon.ico') 1px center no-repeat;
    border: 1px solid #ccc;
    max-width:500px;
  }
</style>
</head>
<body>
<?php// input parameters: twitterName or search$twitterName = (isset($_GET['twitterName']))?$_GET['twitterName']:'';$search = (isset($_GET['search']))?$_GET['search']:'';
if ((
$twitterName=='')&&($search=='')) {
    echo 
'<h2>Please provide a Twitter Name or a search pattern</h2>';
    echo 
'</body></html>';
    exit;
}
$tweetbtn='';
if (
$search!='') {
    echo 
"
    <div align='center'>
    <form name='f1' action='getTweets.php' method='GET'>
    <input type='text' name='search' value='$search' class='inputText' />&nbsp;&nbsp;&nbsp;
    <input type='submit' name='btns' id='btns' value='search' class='inputBtn' />
    </form>
    </div>"
;
    if (
$search[0]=='#'$search substr($search,1);
    
$tweetbtn="?button_hashtag=$search";
}
if (
$twitterName!='') {
    echo 
"<div class='tweets' id='infoUserAccount'><div align='center'><img src='/social/twitter/wait.gif' width='48' height='48' border='0' /></div></div><br/>\n";
    
$tweetbtn="?screen_name=$twitterName";
?><div class="tweets"> <div class="tweets_header">Mini <a href="http://minitwitter.webdevdesigner.com">Tweets</a></div> <div class="content_tweets"> </div> <div class="tweets_footer"> <a href="#"><span id="bird"></span></a> </div> </div>

<script type='text/javascript'>
<?php 
if ($twitterName!='') { ?>jQuery(".content_tweets").miniTwitter("<?php echo $twitterName ?>");
var url = '/social/twitter/getUserInfoTwitter.php?twitterName=<?php echo $twitterName ?>';
$.get(url, function(data){
document.getElementById('infoUserAccount').innerHTML='<b>' + data.name + '</b><br/>' + 
    '<a href="https://www.twitter.com/#!/'+data.screen_name+'" target="_blank">@' + data.screen_name + '</a><br/>' + 
    data.description + '<br/>' + 
    data.location + '<br/>' + 
    data.statuses_count + ' Tweets<br/>' + 
    data.friends_count + ' Following<br/>' + 
    data.followers_count + ' Followers<br/>' + 
    data.listed_count + ' Listed<br/>';
}, "json");
<?php } else { ?>jQuery(".content_tweets").miniTwitter({query: "<?php echo $search ?>"});
<?php ?><br/>
</script> 
<div id="custom-tweet-button" align="center">
  <a href="https://twitter.com/intent/tweet<?php echo $tweetbtn ?>" target="_blank">Tweet</a>
</div>
</body>
</html>

In order to get the information on the Twitter account, we are making another asynchronous call to Twitter using JQuery. The page is called getUserInfoTwitter.php and it is doing a REST call to Twitter. E.g. the call http://api.twitter.com/1/users/show/sugarcrm.json will return in a JSON format the information on the Twitter account SugarCRM. But for a cross scripting security issue, you cannot address directly Twitter, you are calling a PHP script located on your server.

getUserInfoTwitter.php
<?php
 function loadURL_cURL($ip,$uri$timeout){
   
$header=array("Host:".$uri);
   
$ch curl_init();
   
curl_setopt($chCURLOPT_URL$ip);
   
curl_setopt($chCURLOPT_HEADERfalse);
   
curl_setopt($chCURLOPT_HTTPHEADER$header);
   
curl_setopt($chCURLOPT_CONNECTTIMEOUT$timeout);
   
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
   
$return_data curl_exec($ch);
   
curl_close($ch);
   return 
$return_data;
}
$twitterName = (isset($_GET['twitterName']))?$_GET['twitterName']:'';if ($twitterName=='') exit;
// get informations on the user
echo loadURL_cURL("http://199.59.149.232/1/users/show/".$twitterName.".json""api.twitter.com"30);?>

Remark
you should not use this exact syntax. I am meeting important DNS resolution failures in my Linux box so I decided to hard code the IP address in the call, which is a worse practice.

You should replace with this code:
<?php
$twitterName 
= (isset($_GET['twitterName']))?$_GET['twitterName']:'';
if (
$twitterName=='') exit;// get informations on the user$url "http://api.twitter.com/1/users/show/".$twitterName.".json";$userInfo curl_init();curl_setopt($userInfoCURLOPT_URL$url);curl_setopt($userInfoCURLOPT_RETURNTRANSFERTRUE);$userInfo curl_exec($userInfo);echo $userInfo;?>


Now you might want to call the page to get the SugarCRM Twitter account page.
http://yourserveur.com/social/twitter/getUserInfoTwitter.php?twitterName=sugarcrm
On the top, you get the twitter account information, then the last 5 tweets:
 If you click on the Tweet link at the bottom, you get the usual new Tweet page, hosted by Twitter, with the recipient pre-filed:


Let's make a search with the pattern SugarCRM.  
http://yourserveur.com/social/twitter/getUserInfoTwitter.php?search=sugarcrm
On the top, you get the search box, you may change the pattern:

If you click on the Tweet button, you are sending a Tweet with the pattern as a hashtag:

Ok, that's nice, now I want this cool features in my Sugar!

This is easy :) Let's create 2 iFrame fields. The first one for the account Tweets, the second one for the search.

The Tweets field
Type: iFrame
Name: tweets
Generated URL: checked
Default value: http://yourserveur.com/social/twitter/getUserInfoTwitter.php?twitterName={twittername_c}
Height: 400



The Tweets Search field
Type: iFrame
Name: tweetssearch
Generated URL: checked
Default value: http://yourserveur.com/social/twitter/getUserInfoTwitter.php?search={twittername_c}
Height: 400


Create a new tab for each field, and test.

Now this is your turn to play :D
Let me know how you are doing!

Monday, October 8, 2012

Slideshare - my last public presentations

My last public presentations are available on http://www.slideshare.net.


Sugar + SocialCRM - SugarCRM Acceleration Paris - 13 Sep 2012
Presentation delivered in Paris (French)




SugarCRM edition communautaire vs commerciale
Webcast (French), the recording will be posted soon to the SugarCRM web site.

Open World Forum 2012

Hi, sorry for not blogging those last (very busy) days.

I will attend a panel on Thursday 11th October at the Open World Forum in Paris. The topic is "Open Source Web Application on The Cloud".
Links:
The Open World Forum web site,
my session.

And I will post asap the source codes I presented during my session "Sugar + SocialCRM" at the CRM Acceleration in Paris on 13th September.