Sugar Developer Blog

Troubleshooting JSON Errors in the Email module

03 Sep, 2008
Posted By: andreassandberg

Last week I was trying to diagnose an issue in Sugar 5.0 where the email folders within the Email module would not display.  The folders are actually rendered after the Email module has been loaded through an asynchronous JSON call.  So, my first inclination to diagnose the issue was to fire up firebug and inspect the response from the server.  The response looked correct, no PHP warnings or errors were included and the JSON response looked correctly formatted.

After further inspecting the JavaScript code, I found the error was occurring because the eval() call on the response text was failing.  So I was a bit concerned and modified the server code to send back a regular string.  Still the eval() call failed and I was left scratching my head.  I then started thinking that perhaps there was some garbage output or empty spaces after the PHP closing tag (?>) that might have been corrupting the response from the server.  I wrote a quick script to examine all of my files but still came up empty.

Since I knew this error had just recently started happening, I checked out an older revision of my code base from my local SVN repository and sure enough, the old code base worked fine.  The diffs between the two code bases revealed no big changes that should have been causing the issue.   So I systematically started copying files from the broken version of the code base to the working code base until I could re-create the issue.

I finally found the file that was causing the problem.  It was the main language string file that Sugar uses (include/language/en_us.lang.php).  However comparing the different versions using WinMerge revealed no differences in the file.  This was clearly very peculiar.  I then opened up a hex editor and took a peak and finally found the source of my problems.

I found that the broken file contained what is referred to as a byte order mark (bom) which is usually invisible.  The mark can sometimes be added by external editors (Notepad) and can cause headers to be sent to a client even though no echo() or print() statements have been used.  After removing the bom, everything worked as expected.

To ensure that none of my other files were corrupted, I put together a quick script to examine my code base.  I’m including the script below, but please be aware that the normal “use at your own risk” restrictions apply.  Although I’ve still to track down who or what program was responsible for inserting the bom, hopefully this post will save you some time if you see similar symptoms as it was quite tricky to trace down the exact cause.

For more information regarding the byte-order mark, Wikipedia has a quick introduction.

PHP Script to Check for Unwanted Byte Order Marks

<?php
$directory = “include/language”;

$bad_files = array();

$recursive_check = TRUE;
$fix_file = TRUE;

scanDirectory($directory,$bad_files);

if(count($bad_files) > 0 )
{
echo “\n\n\nFound the following files with a bad BOM:\n\n”;
printResults($bad_files);
}
else
echo “No Errors Found”;

function scanDirectory($directory, &$bad_files)
{
global $recursive_check;
$ignore_files = array(”.”, “..”, “.svn”);

if ($handle = opendir($directory))
{
echo “\nChecking Directory: $directory\n”;

while (false !== ($file = readdir($handle)))
{
$fullFile = $directory . DIRECTORY_SEPARATOR . $file;

//Recursive Call for sub-directories.
if( is_dir($fullFile) && $recursive_check )
{
if( in_array($file, $ignore_files) )
continue;
scanDirectory($fullFile, $bad_files);
}
if( checkFile($fullFile))
$bad_files[] = $fullFile;
}

closedir($handle);
}
}

function checkFile($file)
{
global $fix_file;
if(!is_file($file))
return;

echo “\t\t$file”;

$isBad = FALSE;
$file_contents = file_get_contents($file, “+r”);

$hex = bin2hex($file_contents);
$first_token = substr($hex,0,6);

if($first_token == ‘efbbbf’)
{
$isBad = TRUE;
echo “……FOUND ERROR\n”;

if($fix_file)
fixFile($hex,$file);
}
else
echo “……OK\n”;

return $isBad;
}

function printResults($results)
{
foreach ($results as $fileName)
echo “$fileName\n”;
}

function fixFile($hex, $file)
{
$fp = fopen($file, ‘w’);
$good = substr($hex,6);
$good_string = pack(’H*’, $good);
fwrite($fp, $good_string);
fclose($fp);
}

?>

Sugar Data Center Edition and automating a hosting environment

03 Sep, 2008
Posted By: nate

The first release of Sugar Data Center Edition is nearing completion in the next couple of weeks.  As the engineering team is going through the final rounds of testing I thought I would shed some light on what we are trying to accomplish with this new product line.

SugarCRM as a company is now over four years old.  We have been running our Sugar On-Demand business since the beginning and have learned a lot along the way as our customer base and hosting environment has grown.  We began by creating some automation in our environment for deploying Sugar On-Demand instances, then adding some reporting and admin automation for our sales and support teams.  After our IT team built a UI on top of their work, we decided that it was time to productize our internal efforts and release it as Sugar Data Center Edition (DCE).

There are two audiences in the Sugar community that we are targeting with this new product line.  The first one is our Sugar partners who are reselling and hosting Sugar products in their own data centers around the world.  The second audience is our larger multi-national enterprise customers who are rolling out larger company-wide Sugar deployments across several distinct divisions and geographical locations.

DCE is built on top of the same Sugar Platform that our Community, Professional and Enterprise editions run on.  The product serves as an administration console for managing many Sugar instances in a hosting environment.  The levels of automation that we are initially targeting are:

  • deployments
  • upgrades
  • support user management
  • licensing
  • monitoring/data collection

To further extend the automation aspects that DCE provide, we have also extended our Web Services API for some DCE-specific function calls.  This is primarily targeted to our partner base who would like to have their already existing systems such as their own corporate Web sites and billing systems perform automated calls without having to log into the system manually to perform certain actions.., a couple use cases could include:

  • A prospect signing up on a partner’s corporate site could fire off an automated call to DCE which will provision and license the evaluation copy and then notify the prospect when it is live and ready to be used.., typically within a couple minutes
  • A customer who is updating their details via an existing billing system can then have the billing system call out to the DCE instance and update the customer information in there to keep updated licensing/billing information

There are a hefty amount of features we are digging through and organizing for some future releases targeted for later this year and early next.  If you have some ideas for some DCE functionality that would help you in your Sugar hosting needs, drop them in the comments.

Module Builder vs Custom Fields

02 Sep, 2008
Posted By: David Wheeler

Something to watch out for when working in Module Builder are reserved database keywords. In Studio, this isn’t a problem as all the custom fields get ‘_c’ appended to their column names in order to easily distinguish them from out-of-the-box fields. In Module Builder however, fields are stored with the exact name entered, so database keywords such as “IN” “AND” “SELECT” are disallowed as a field name.  Module Builder will notify you that a field name is not allowed because it is a database reserved word.  Studio doesn’t need to give that error notification because all fields are automatically appended with the ‘_c’.

Entry Points in Sugar 5.1

29 Aug, 2008
Posted By: John Mertic

One of the many code re-factoring changes we’ve made with Sugar 5.1 is to consolidate the number of entry points into the application as well as rerouting the current entry points through the MVC framework. An entry point is a PHP file that can be called either via URL or command line to actually invoke a Sugar process.  For instance when calling the home page of the application via URL or starting the scheduler via command line.  Consolidating the entry points has also helped us secure the application better and improve quality by making sure each request goes through the same initialization code.

Backwards compatibility with custom code
It does however present some backwards compatibility problems.  Most notably you will need to update your code if you have custom code that relies on a deprecated entry point such as a custom Quote template that may have called pdf.php which is no longer a stand-alone entry point. In these cases, you will need to change the URL reference as described below:

  1. Look for the entry point file name in the include/MVC/Controller/entry_point_registry.php. It will be the in the ‘file’ key in the sub-array.
  2. Make note of the key of that array. So for the pdf.php entry point, it appears in include/MVC/Controller/entry_point_registry.php as:
    'pdf' => array('file' => 'pdf.php', 'auth' => true),

    So we will want to use the ‘pdf’ part in the next step.

  3. Change the URL reference from the current reference to one in the form of:
    index.php?entryPoint=<<entrypoint>>

    So for the above pdf.php example, we would change our references from

    pdf.php

    to:

    index.php?entryPoint=pdf

The only remaining entry points in 5.1 that aren’t using this new index.php URL pattern (and therefore continue to be valid entry points) are:

  • campaign_tracker.php – used by the Campaign Management module for tracking campaign responses. Deprecated as of Sugar 5.1.0.
  • cron.php – used by the Windows Scheduler Service or the cron service on Linux and Unix for executing the Sugar Scheduler periodically.
  • index.php – default entry point into the Sugar application
  • install.php – used for initial install
  • maintenance.php – invoked when the application is down for maintenance.
  • metagen.php - Deprecated as of Sugar 5.1.0.
  • silentUpgrade.php – used for silent installer
  • soap.php – entry point for all SOAP calls
  • vcal_server.php – used for syncing information to Outlook

Module Builder - What’s new in 5.1

28 Aug, 2008
Posted By: David Wheeler

One of the best parts of using Sugar has to be customizing it. You don’t want to use a software package if it can’t meet your needs, and no two people or businesses have the same set of requirements. Sugar provides some really neat tools when it comes to customization, and in 5.1 they have been made even better.

There are a lot of really cool additions to the 5.1 developer tool set, and I’d thought I’d take a minute to quickly go over a couple of them.

Once you open up Module Builder in 5.1, you will notice the addition of two new templates, File and Sale. The File template is for document-based modules such as resumes, photos, or any other files you need to work with. Sale is based on the opportunities module and is designed to store information on sales and possible future sales.

After you have created a new module, you may also notice a few more new things. One that I would like to point out is under layouts, we have added Sugar Dashlet views. Starting in 5.1, your new custom modules will have Sugar Dashlets available straight out of Module Builder. You can edit the Sugar Dashlet layouts in the same manner as standard list and search views.

Finally, I’d like to touch on the area of integrations. In previous versions of Sugar, there wasn’t an easy way to integrate with existing web based systems from Studio or Module Builder. Certainly you can find many tutorials on how to add custom logic hooks to do this, but there hasn’t been a way to do integrations writing any code. In 5.1, this has changed with the addition of Link fields and iFrames with auto-generated URLs.
I’ll show how this is used with a fairly simple example, adding a Google map to the DetailView of a company. In this case my new “Partners” module which is based on the company template.

  1. Open up your custom module’s fields, and click “Add Field”.
  2. Select the IFrame field type.
  3. Fill in the following data:
    • FieldName: GoogleMap
    • Display Label: Google Map
    • Generate URL: check
    • Default Value (now this is where the magic happens):
      http://maps.google.com/maps?f=q&q={shipping_address_street}+
      {shipping_address_city}+
      {shipping_address_state}+
      {shipping_address_postalcode}+
      {shipping_address_country}
    • Max Size: 255
    • IFrame Height: 400
  4. Add the new field to your DetailView. Be sure to give the field a full row to itself for maximum readability. Note that adding an iFrame or a Link field that has a generated URL to an EditView will result in a read-only field that is un-editable.
  5. Deploy your package and create a new record in your new module, be sure to enter a real address into the shipping address fields.
  6. Save the record and view the Google map pointing to the business’ location.

The uses for generated URLs are almost endless, as you can embed links and iFrames to either internal or external systems without writing a single line of code. Feel free to post some ideas for other integrations.

Adding items to the Shortcuts menu

27 Aug, 2008
Posted By: kaz

Want to add items to the shortcut menu?  No problem.  Create the following file

custom/modules/<module name>/Ext/Menus/menu.ext.php

Here is an example of the contents:

<?php
$module_menu[] =Array(”index.php?module=Reports&action=index&view=contacts”, $mod_strings[’LNK_CONTACT_REPORTS’],”ContactReports”, ‘Contacts’);
$module_menu[] =Array(”javascript:SUGAR.subpanelUtils.loadSubpanelGroup(’Activities’);”, $mod_strings[’LBL_ACTIVITIES’],”Contacts”);

?>

The $module_menu array contains 4 elements:

  • The first element is target of the click.  It can either be a URL or Javascript
  • The second element is the text to display in the menu.  This must obviously be defined in a language file
  • The third element is the icon to display next to the shortcut.  The icons can be found in the themes\default\images directory.
  • The fourth element (which is optional) is the name of the module.

In the examples above, the first line takes you to Contact Reports.  The second example changes the tab in the detail view to make the Activities subpanel have focus

Importing into custom modules in 5.1

27 Aug, 2008
Posted By: John Mertic

The Import functionality in Sugar has had tremendous overhaul in Sugar 5.1 to make it a more robust and more user-friendly tool. Among the many improvements made is the ability to leverage the import tool very easily for custom modules.

Enable the module for import

To begin with, enabling a module for import can be easily done through Module Builder. When creating or editing a module’s properties, an new option entitled ‘Importing’ has been added. By checking this option, the import utility will be enabled for the module.

Module Builder showing the \

When you deploy the newly created package, a new option will appear in the left sidebar called “Import”, which will trigger the built-in Import function for the module.

You can also manually enable Importing for a module with two simple changes to your code. The first change is to the module’s bean file, where you’ll need to add the line of code below inside the bean class.

var $importable = true;

Next, you’ll need to add a line similar to what’s below into the Menu.php file, to add the left sidebar menu option that allows a user to import into the module.

if(ACLController::checkAccess('<<module name>>', 'import', true))
$module_menu[]=Array(
"index.php?module=Import&action=Step1&import_module=<<module name>>&return_module=<<module name>>&return_action=index",
$app_strings['LBL_IMPORT'],"Import", '<<module name>>');

Controlling the importable fields

You can also have fine-grained control over the fields you wish to import by optionally disabling a field from being imported into or requiring a field to be specified in the import file. Again, this can be controlled from Module Builder in the properties for each field. You can also set this option for custom fields you to the existing core modules through Studio.

Module Builder adding a new field with the \

The Importable option has three possible values: “Yes” indicates that you can import into the field and “No” indicates you can’t. In addition, setting it to “Required” forces users to specify a value for that field when importing.

You can also set this value manually in the vardefs, by setting the ‘importable’ option for the field you wish to change. SugarCRM assumes the field is importable, but not required, if no option is set ( the equivalent of the ‘Yes’ value of the importable flag as shown above ).

'new_text' => array (
'required' => false,
'name' => 'new_text',
'vname' => 'LBL_NEW_TEXT',
'type' => 'varchar',
'massupdate' => 0,
'comments' => '',
'help' => '',
'importable' => 'false', // don't allow importing into this field
'duplicate_merge' => 'disabled',
'duplicate_merge_dom_value' => '0',
'audited' => 0,
'reportable' => 1,
'len' => '25',
),
'new_text_2' => array (
'required' => false,
'name' => 'new_text_2',
'vname' => 'LBL_NEW_TEXT_2',
'type' => 'varchar',
'massupdate' => 0,
'comments' => '',
'help' => '',
'importable' => 'required', // this field is required for importing
'duplicate_merge' => 'disabled',
'duplicate_merge_dom_value' => '0',
'audited' => 0,
'reportable' => 1,
'len' => '25',
),

The improvements to the Import module in SugarCRM 5.1 allow you to easily leverage the powerful import utility with a few simple changes to your existing modules.

Enabling IMAP support under OS X Leopard

26 Aug, 2008
Posted By: John Mertic

With the release of Mac OS X 10.5 Leopard last fall, Apple (finally) included a modern version of PHP (currently version 5.2.6 as of this writing). With it comes several improvements in the areas of performance, web services support (including extensions for JSON and XML), OOP, and error handling. However, the default install of PHP that Apple included didn’t include all of the available PHP extensions. Most notable is the IMAP extension, which is used in SugarCRM for the Campaigns and Emails modules. However you can build this extension and have it loaded dynamically, without affecting the rest of the default PHP install.

First, we’ll need to compile the c-client imap libraries, which can be obtained at ftp://ftp.cac.washington.edu/imap/. Notice the make command below; it has extra arguments added to be sure we are building the libraries as universal binaries, which is important since mod_php and apache are both compiled for 64-bit architecture.

$ curl -O ftp://ftp.cac.washington.edu/imap/c-client.tar.Z
$ tar -zxvf c-client.tar.Z
$ cd imap-2007b
$ make oxp EXTRACFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os  -pipe -no-cpp-precomp"

We’ll then copy the libraries we built to the /usr/local directory, which is a place we can put any user-built programs and libraries and won’t affect the base system install.

$ sudo cp c-client/*.h /usr/local/include/
$ sudo cp c-client/*.c /usr/local/lib
$ sudo cp c-client/c-client.a /usr/local/lib/libc-client.a

Next, we’ll grab the PHP tarball for the version of PHP we are using, which is currently 5.2.6, from the source: http://www.php.net/downloads.

$ curl -O http://www.php.net/distributions/php-5.2.6.tar.bz2
$ tar -xjf php-5.2.6.tar.bz2

Now we’ll go to the source directory of the imap extension and compile it. Again you’ll notice the additional agruments added for building the extension as a universal binary. ( Edit: note that the configure line below is multiple lines; each new command to enter is denoted by the $ at the beginning of the line, although the $ is not part of the command ).

$ phpize
$ MACOSX_DEPLOYMENT_TARGET=10.5 \
CFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os  -pipe -no-cpp-precomp" \
CCFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os  -pipe" \
CXXFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64 -g -Os  -pipe" \
LDFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64 -bind_at_load" \
./configure --with-kerberos --with-imap-ssl
$ make
$ sudo make install
$ sudo echo "extension=imap.so" >> /etc/php.ini

Also, you’ll want to comment out the line ‘extension_dir = “./”‘ in the php.ini file, since it will interfere with the extension loading.

Finally, restart apache and you should see the imap extension listed in the output of phpinfo().

phpinfo() output showing imap enabled

Migrating data into Sugar with file attachments using Talend

25 Aug, 2008
Posted By: briankilgore

As a member of the Professional Services team here at Sugar, one of the services that is often requested of us is migrating a client’s data from their existing CRM into Sugar. In the past, our team would write custom migration scripts that read their data, performed some basic cleansing, transformed it into Sugar format, and then imported it into the database. This was a laborious task given the disparate data formats out there and varying levels of data quality–a task which none of us looked forward to doing.

Recently, however, some of us began experimenting with a tool called Open Studio, which is provided by one of Sugar’s technology partners, Talend. Open Studio is an open source data integration suite licensed under GPLv2 and provides a variety of tools for bridging the gap between disparate applications.

Using Talend Open Studio 2.4, the following are the steps that I followed to setup a migration of Note records from a Salesforce.com CSV file, including copying and renaming the associated attachments. Theser steps will work for Sugar Community Edition, Sugar Professional Edition and Sugar Enterprise Edition.

Note that the copy and rename step isn’t necessary for migrating SFDC Note data since their file attachments are already uniquely named with the record id (as it is in Sugar).  However this step highlights some of the additional functionality provided by Talend and is a useful exercise as other CRM systems don’t save their attachments with a corresponding GUID.

First download and install SugarCRM. Then download and install Talend Open Studio.  Next request a backup of your data from Salesforce.com.

Now open Talend Open Studio and follow these steps:

  1. Setup a tFileInputDelimited object that points to the SFDC Note.csv file
  2. Create a tFileList object that points to the directory that contains your attachment files (setting up the proper filters if more than the attachments exists within the directory)
  3. Create a tFileProperties object
  4. Right click the tFileList object and select Row->Iterate, and drag the line over to the tFileProperties object
  5. Click on the Component tab for the tFileProperties object and click in the File textbox. Press ctrl+space to bring up the contextual selection menu, and select the CURRENT_FILEPATH property for the tFileList object.
  6. Create a tMap object
  7. Right click the Notes tFileInputDelimited object and select Row->Main, and drag the line over to the tMap object
  8. Right click the tFileProperties object and select Row->Lookup, and drag the line over to the tMap object.
  9. Create a tFileCopy object
  10. Create a database output object (i.e. tMysqlOutput)
  11. Right click the tMap object and select Row->*New Output* (main), and drag the line over to the tFileCopy object. Give this output object a unique name (i.e. file_copy).
  12. Right click the tMap object and select Row->*New Output* (main), and drag the line over to the database output object (tMysqlOutput). Give this output object a unique name (i.e. notes)
  13. Double click the tMap object to bring up the map editor.
  14. On the left hand side of the map drag the column that contains the file name of the attachment down to the basename column on the tFileProperties schema. Select Inner Join.
  15. Drag the columns from the Notes schema on the left hand side over to the corresponding notes schema on the Right hand side. Make sure that your schema types are setup properly to avoid runtime errors. (i.e. date fields are set to Date, Boolean fields are set to Boolean, etc)
  16. Create a new column called new_file_name on the file_copy schema.
  17. Drag the column containing the id from the left hand side notes schema over to the new_file_name column on the file_copy schema
  18. Click OK to close the map editor.
  19. Click the Component tab for the tFileCopy object.
  20. Enter the CURRENT_FILEPATH property for the tFileList object into the File Name text box as you did for the tFileProperties object (step 5).
  21. Select your destination directory (for Sugar attachments, this is cache/uploads)
  22. Select the Rename checkbox and enter the new_file_name property for the file_copy object into the Destination filename textbox (for the above scenario it would be file_copy.new_file_name)
  23. Click the Run tab.
  24. Select the Statistics checkbox if you want to see where in the migration the job currently is, plus how fast the data is being migrated over.
  25. Click Run.

This should get you going to handle a basic migration of Notes data with copy and rename of the attachments. Hopefully, you will be able to take this and extend it to handle migration of data from any source into Sugar.

Wrapping up first version of Sugar Developer Guide

22 Aug, 2008
Posted By: Clint

A quick update on the Sugar Developer Guide.  We are wrapping up the first round of editing of the new Sugar Developer Guide and are getting ready for a public review in the next week or so.  I’d like to open up an early review to a few community members who can dedicate some time over the next week to giving the 120 pages a thorough review.  Any takers?  Leave a comment with your email address.

Have feedback for us? Drop us a line.
Terms & Conditions | Privacy | Trademark Info | Contact Info | FAQs | SugarCRM Inc.© 2004 - 2008 All rights reserved.