Skip navigation
All Places > Developer > Blog > Authors SugarCRM Developers
1 2 Previous Next

Developer

23 Posts authored by: SugarCRM Developers

Post originally written by avlasovsugar.

 

Alex Vlasov is an experienced Sugar Developer and contractor that works on the SugarCRM Engineering platform team. In this post, Alex shares advice to Sugar Developers for preparing their Sugar customizations for PHP 5.6 and PHP 7.

 

The Sugar team has been preparing core Sugar code for PHP 5.6 and PHP 7 support with an eye on deprecating support for older PHP versions. This means there are some actions that the Sugar Developer community needs to take in order to prepare their code for latest versions of PHP.

 

Summary of core Sugar app changes

 

In order to support newer versions of PHP, we made some changes to internal core classes that Sugar Developers need to know about. Many of these changes were made in Sugar 7.7.1 when we added PHP 5.6 support. Other changes outlined below are in upcoming Sugar releases.

 

PHP 7 deprecated features removed from core Sugar code

  1. Removed static calls to non-static methods
  2. Removed PHP4-style constructors

 

Read the full list of features deprecated in PHP7.

 

Additional changes to address core Sugar code hygiene and to adopt new PHP 7 features

  1. Fixed incompatible child class methods signatures
  2. Updated 3rd party libraries to support latest versions of PHP
  3. Changes to support PHP 7.0 new uniform variable syntax
  4. Adopted PHP 7's CSPRNG API but added random_compat library to support older PHP versions

 

Actions for Sugar Developers

 

In order to properly upgrade custom code, two main tasks need to be performed:

 

Make your code compatible with PHP 5.6 and PHP 7.0

 

To make your code compatible with PHP 5.6 and 7.0, use the following checklist.

  1. Remove static calls to non-static methods within your PHP code
  2. Remove any use of PHP4-style constructors within your PHP code
  3. Remove other deprecated PHP functionality in your PHP code (PHP 5.6 deprecated features, PHP 7 deprecated features)

 

For example,

 

class Foo {

public function __construct() { // Yes.
// ...
}

public function Foo() { // No.
// ...
}

    public static function aStaticMethod() {
        // ...
    }

    public function aNormalMethod() {
    // ...
    }

}

Foo::aStaticMethod(); // Yes.

Foo::aNormalMethod(); // No.

 

 

 

For additional detail, refer to PHP migration guides for PHP 5.6 and PHP 7.0.

 

Make your code compatible with changes to Sugar PHP APIs

 

To make custom code compatible with updated Sugar PHP APIs, use the following checklist.

  1. (7.7.1) Do not use old PHP4 constructors in Sugar PHP classes, they are removed as part of PHP 7.0 support.
  2. (7.7.1) If there is code that extends Sugar PHP classes, make sure child methods signatures are correct
  3. (7.7.1) 3rd party libraries were moved:
    1. parsecsv library was moved to the vendor directory
  4. (future) 3rd party libraries updates:
    1. Elastica library will be upgraded, so make sure you do not use deprecated Elastica APIs.
    2. Removal of mcrypt support and Crypt_Blowfish library, so make sure you are not calling these libraries directly.
  5. (future) Significant changes to be made to the following Sugar PHP APIs:
    1. ActivitiesApi::getQueryObject() will become protected and function arguments are changed
    2. RelationshipHandler class will stop extending Relationship class
    3. SearchForm class will not extend EditView class anymore
    4. Quarter*TimePeriod::buildLeaves() methods will have function arguments changed
    5. PMSEEngineFilterApi::addFieldFilter() method will be renamed

Post originally written by dwheelz.

 

Here is an important message from David Wheeler, a long time Software Engineer and Architect at SugarCRM, about using e-mail fields correctly.

 

E-mail handling is core to CRM software. Almost everyone we know uses multiple e-mail addresses every single day for both personal or work purposes. So it goes without saying that managing a person's multiple e-mail addresses correctly is essential in your Sugar customizations and integrations.

 

History of Sugar E-Mail fields

 

Several years ago, Sugar changed from using email# named text fields (like email1, email2, etc.) to using an e-mail relationship. This was done to better handle multiple e-mails, multiple relationships, and e-mail attributes like opt in or invalid.

 

However, use of the email1 field remains particularly persistent. We observe many examples of custom code (and some core code) that still use the old email# fields. This is probably because it is convenient to use the email1 field like a regular text field.But this is out of date, inaccurate, deprecated, and subject to removal in upcoming Sugar releases.

 

Below we will describe the proper method for using e-mail fields within Sugar customizations and integrations.

 

Sugar Metadata

 

You should reference the "email" field instead of "email#".

 

For record views, this will load a "email" type field widget with all related e-mail addresses included.

 


 

For list views, instead of all e-mail addresses only the primary e-mail address will be displayed.

 


 

Sugar PHP code

 

Instead of

$bean->email#

 

use

$bean->emailAddress->addresses

 

which references an array of e-mail addresses.

 

To determine the primary e-mail, you can iterate over the addresses array to find where the primary_address attribute is true.

foreach ($bean->emailAddresses->addresses as $address) {
    if ($address->primary_address == true) {
        // Found primary e-mail
    }
}

 

v10 REST API

 

When selecting a record's email field in a GET request, it will return a JSON array of all associated e-mail addresses.

 

When using a PUT request to update an e-mail address on a record, provide the complete e-mail address array.

 

For example,

"email": [
    {
        "email_address": "judith.banks@yahoo.com",
        "primary_address": true,
        "reply_to_address": false,
        "invalid_email": false,
        "opt_out": false
    },
    {
        "email_address": "jbanks@hotmail.com",
        "primary_address": false,
        "reply_to_address": false,
        "invalid_email": false,
        "opt_out": true
    }
],

 

If you leave off an e-mail address in a PUT request then this will be removed during the update to the record.

 

Sidecar code

 

For Sidecar code, you should not reference fields like email1, etc, when working with Beans or other models. You should be referencing the email field instead.

model.get("email1") --> model.get("email")

 

This will return a JavaScript array in the same format as returned by the REST API (above). This array of e-mails is typically iterated through for display in custom Handlebars templates.

 

PDF Templates

 

This change also applies to PDF templates. Here is the correct way to reference the primary e-mail address from a PDF template.

{$field.email1} --> {$fields.email_addresses_primary.email_address}

 

Other locations

 

You may still see email1 field referenced in e-mail templates used with Advanced Workflow or Campaigns module. Don't count on those sticking around. Please, use the email field instead.

Post originally written by mrussellsugarcrm.

 

 

Sugar REST PHP Client

 

A new open source library for working with Sugar 7's powerful REST API has just been published! You can view the library in our GitHub account here: https://github.com/sugarcrm/rest-php-client

 

Full instructions for installation, usage, current included API Endpoints, and ways to contribute can all be found in the GitHub Wiki on the repository.

 

Who should use it?

 

The Sugar REST PHP Client was built initially to make working with Sugar instances easier for some of our internal tools. I wanted to provide a quick, easy, and object oriented, way to access Sugar 7's REST API that could be used by all PHP developers. Any developer who is developing PHP applications that integrate with Sugar 7 should be interested in using the new library. The simplified architecture takes away the hassle of setting up and managing Curl connections, and allows the developer to focus on what matters most, which is working with the data in their Sugar application.

 

How to use it?

 

The REST PHP Client is setup as a composer package, and can be easily added to your existing project by adding it to your required packages in your projects composer.json file.

 

Once it's installed via composer, you can access the Client in the SugarAPI\SDK namespace.

 

A quick example of how much easier to use this new library is, can be shown by reviewing the latest documentation on using the REST API to get a filtered list of records from a module in the Support Developer Guide, and comparing it to the following snippet of code that does the exact same API request.

$server = 'https://localhost/sugarcrm/rest/v10/';

 

$credentials = array(

 

    'username' => 'admin',

 

    'password' => 'asdf'

 

);

 

$SugarAPI = new \SugarAPI\SDK\SugarAPI($server, $credentials);

 

$SugarAPI->login();

 

$requestData = array(

 

    "filter" => array(

 

    array(

 

        '$or' => array(

 

           array(

 

                //name starts with 'a'

 

                "name" => array(

 

                    '$starts'=>"A",

 

                )

 

            ),

 

            array(

 

                //name starts with 'b'

 

                "name" => array(

 

                    '$starts'=>"b",

 

                    )

 

                )

 

            ),

 

        ),

 

    ),

 

    "max_num" => 2,

 

    "offset" => 0,

 

    "fields" => "id",

 

    "order_by" => "date_entered",

 

    "favorites" => false,

 

    "my_items" => false,

 

);

 

$response = $SugarAPI->filterRecords('Accounts')

 

                     ->execute($requestData)

 

                     ->getResponse();

 

if ($response->getStatus()=='200'){

 

    $records = $response->getBody();

 

    print_r($records);

 

}

 

Each API Endpoint in the Sugar 7 REST API is defined by an Endpoint Class in the REST PHP Client library, which is then dynamically mapped to a method on the REST PHP Client Object. This dynamic method, generates the Endpoint Object, configures the Curl Request, and returns the Endpoint Object for manipulation, such as loading in data and finally executing the actual request.

/**

 

Calling filterRecords() dynamic method, returns a

 

SugarAPI\SDK\Endpoint\POST\ModuleFilter Object

 

**/

 

$SugarAPI = new \SugarAPI\SDK\SugarAPI($server, $credentials);

 

$SugarAPI->login();

 

$FilterEndpoint = $SugarAPI->filterRecords();

 

These Endpoint Objects manage the Request and Response to the API, as well as manage the data sent to the API. As shown above the execute() method on the Endpoint Object takes the request data that is to be sent to the Sugar API, and submits the Request the server. In the Filter Record example above, the passed in data matches what is shown in the API Endpoints documentation, however this is not always the case. The Endpoint Classes allow for manipulation of the data before being added to the Request, which means that the REST PHP Client can shorten the needed payload to allow integrated systems to quickly build requests.

 

One such example of this is the Bulk API Endpoint Object included in the REST PHP Client. As the documentation shows, the API request payload can be quite complicated, so to simplify it, it seemed intuitive to make the execute() method on the Bulk Endpoint Class, accept an array of REST PHP Client Endpoints objects, as they contain all the data needed for the Bulk request, saving the developer time by not having to build out the complex payload manually.

$SugarAPI = new \SugarAPI\SDK\SugarAPI($server,$creds);

 

$SugarAPI->login();

 

$Accounts = $SugarAPI->filterRecords('Accounts')

 

                     ->setData(array('max_num'=> 5));

 

$Contacts = $SugarAPI->filterRecords('Contacts')

 

                     ->setData(array('max_num'=> 1));

 

$Notes = $SugarAPI->filterRecords('Notes')

 

                  ->setData(array('max_num'=> 3));

 

$Leads = $SugarAPI->filterRecords('Leads')

 

                  ->setData(array('max_num'=> 2));

 

$BulkCall = $SugarAPI->bulk()->execute(array(

 

    $Accounts,

 

    $Contacts,

 

    $Notes,

 

    $Leads

 

));

 

$response = $BulkCall->getResponse();

 

if ($response->getStatus()==‘200’){

 

    print_r($response->getBody());

 

}

 

As of right now, not all Endpoints have been added to the REST PHP Client library, however the major Endpoints used for manipulating data have been added, and more will be added in continued releases. Check out the current Endpoints that can be used here which also includes a short code snippet showcasing how to use each one.

 

What's Next?

 

I would love to see more PHP developers using the library, which is why it is being released as an Open Source community project. Any issues that are found can be added to the GitHub repository under the Issues tab. Likewise, if you have features you would like added you can add them to the Issues tab on the repository as well. More detailed contribution guidelines can be found in the CONTRIBUTING doc, and in the Wiki.

Post originally written by Vadzim Ramanenka.

 

Sugar Performance Engineer Vadzim Ramanenka shares some tips for profiling Sugar code using our newly launched SugarCRM XHProf Viewer open source project.

 

Profiling Sugar softly

Whenever you encounter that something is not working as fast as you would like, you need a way to look “under the hood” and understand what exactly is slowing things down. Sugar 7.7.0 added a built-in capability to collect detailed performance profiling data and store it in files. Recently, we released an open source SugarCRM XHProf Viewer project which can be used to visualize performance bottlenecks.

 



Read on to learn how to configure both Sugar and the Viewer in a few simple steps.

 

Prerequisites

You need to have a copy of Sugar on-site installed where you have file system access such as on your local machine. We rely on the xhprof php extension so you are required to have it installed on the version of PHP configured with Sugar as well.

 

Enabling XHProf logging in Sugar

First of all we have to create a directory which Sugar will use to store profiling information. I use /tmp/profile_files/sugar in this example but you can choose any location. Just make sure the target directory exists and that Apache has write permissions to it.After that, there are a few lines of configuration should be added to the config_override.php file to ask Sugar to start collecting profiling data. This is an example of a bare minimum configuration:
$sugar_config['xhprof_config']['enable'] = true;$sugar_config['xhprof_config']['log_to'] = '/tmp/profile_files/sugar';
By default, Sugar profiles only 10% (1 in 10) of the incoming requests. If you want to profile all requests then add this line to config_override.php:
$sugar_config['xhprof_config']['sample_rate'] = 1;
For additional configuration options please refer to the xhprof_config setting documentation in the Sugar Developer Guide.Now open Sugar and start clicking around. If you’ve done everything right files should start appearing in /tmp/profile_files/sugar.

 

Configuring SugarCRM XHProf Viewer

Download the latest release of the Viewer and extract it somewhere under your server's web root directory.By default, the Viewer looks for profiling data under /tmp/profile_files and subdirectories. If you want to change this then create a config_override.php file in the Viewer's root directory with the following content:
<?php$config['profile_files_dir'] = '<PROFILE FILES LOCATION>';
And replace <PROFILE FILES LOCATION>  with the directory that you specified in Sugar’s config_override.php.This is pretty much it. Have fun!

 

SugarCRM XHProf Viewer Screenshots

 

There are three different dimensions of information collected for every single request. Check out the screenshots below.

 



 



 



Post originally written by Emmanuel iNet.

 

Here is another guest post from Emmanuel Dyan from the Elite SugarCRM Partner iNET Process. In it he addresses a common Sugar project requirement using an open source tool developed by iNET Process.

 

The problem that we will try to solve with this post is:

How do we make sure that we are never developing using actual customer data but, at the same time, work with data that reflects reality?

 

Data Anonymization

 

Usually, when we work on a customization project for a customer you have a minimum of 3 different environments: Development,  User Acceptance Testing (UAT), and Production. To make sure that we work in optimal and consistent conditions, we usually copy the database from one environment to another (preferably from production to other environments). Doing this type of manipulation has multiple drawbacks, including:

  • We have to collect a Database Dump which means that it contains unencrypted raw data. What would happen if we mistakenly expose this dump to someone who is unauthorized?
  • We have to test some functionality to make sure that it works. What would happen if we test a Campaign that sends thousand of e-mails ... to the ... actual customers of our customer?

 

Anonymizing the data is the best practice to avoid "playing" with customer data and to keep their trust in us.

 

The challenge with anonymizing data is figuring out how to overwrite the data with something that is completely unrecognizable. For example: "John Doe" will become "voluptatem accusantium". His job title becomes "doloremque" and his country "magnam". His phone number will become "569898520114457878744778" instead of "+123456789".

 

Anonymization Tool

 

Now how do we work in realistic conditions with that kind of anonymization? Indeed, we need another solution that works with any Sugar instance. The solution we are demonstrating below is implemented in our open source CLI tool (sugarcli).

 

The anonymization architecture follows:

  • It uses another independent tool, called "neuralyzer" (by iNet Process) that is a command line tool to anonymize a database.
  • Neuralyzer uses a library called "Faker" to generate realistic data.
  • It is composed of two subcommands:
    • A configuration generator.  This is used to generate a configuration file automatically without destroying the system tables (config, relationships, etc).
    • The anonymizer that connects directly to the Sugar Database to perform the job. It uses iNET Process libsugarcrm to performs SQL queries (via a PDO connection). It also purges deleted records and cleans _cstm tables of deleted records. It finishes by then emptying all _audit tables.

 

Configuration Generator

 

The configuration generator reads all the Sugar database tables and tries to guess, from the field type or the field name what type of fake data needs to be generated. For example a field containing the string city will receive a random city.  If a field contains _user_id then it will be ignored in order to preserve unique IDs.

 

To use it, first download SugarCLI from https://github.com/inetprocess/sugarcli.

 

Then run:

./sugarcli.phar anonymize:config --path <sugarcrm_path>

 

The parameters are the following:

    --file=FILE                   Path to the configuration file [default: "../db/anonymization.yml"]    --ignore-table=IGNORE-TABLE   Table to ignore. Can be repeated (multiple values allowed)    --ignore-field=IGNORE-FIELD   Field to ignore. Can be repeated (multiple values allowed)-p, --path=PATH                   Path to SugarCRM installation.

 

Example

 

The command ./sugarcli.phar anonymize:config creates a file that looks like:

guesser_version: 1.0.0 entities:     accounts:         cols:             name: { method: company }             description: { method: sentence, params: [20] }             facebook: { method: url }             twitter: { method: url }             googleplus: { method: url }             account_type: { method: randomElement, params: [['', Analyst, Competitor, Customer, Integrator]] }             industry: { method: randomElement, params: [['', Apparel, Banking, Biotechnology, Chemicals]] }             annual_revenue: { method: randomNumber, params: [4] }             phone_fax: { method: phoneNumber }             billing_address_street: { method: streetAddress }             billing_address_city: { method: city }             billing_address_state: { method: state }             billing_address_postalcode: { method: postcode }             billing_address_country: { method: country }             rating: { method: sentence, params: [8] }             phone_office: { method: phoneNumber }             phone_alternate: { method: phoneNumber }             website: { method: url }

 

As you can see, the commands uses different methods to guess the type of faker methods to use:

  • If it is a dropdown then it gets the list of values from vardefs
  • If the field contains a known word it uses a pre-defined method (example .*_city = city)
  • Otherwise it will use the dbType (varchar = sentence)

 

You can change the content of the file once generated to match your criteria.

 

Run the anonymization

Warning! Do not run this on a production instance! This command will overwrite data in the target database. 
./sugarcli.phar anonymize:run --path <sugarcrm_path>

 

The parameters are the following:

    --file=FILE        Path to the configuration file [default: "../db/anonymization.yml"]    --force            Run the queries    --remove-deleted   Remove all records with deleted = 1. Won't be launched if --force is not set    --clean-cstm       Clean all records in _cstm that are not in the main table. Won't be launched if --force is not set    --sql              Display the SQL of UPDATE queries    --table=TABLE      Anonymize only that table (repeat for multiple values) (multiple values allowed)-p, --path=PATH        Path to SugarCRM installation.

 

 

 

Example

 

The command ./sugarcli.phar anonymize:run --table=accounts --force gives the following output:

Be careful, the anonymization is going to start That will overwrite every data in the Database !
If you are sure, please type "yes" in uppercase YES
Anonymizing accounts50/50 [============================] 100%
Emptying accounts_auditEmptying bugs_auditEmptying campaigns_auditEmptying cases_auditEmptying contacts_auditEmptying contracts_audit....
Done in 0.42 sec (consuming 40.5Mb)
Now you can dump your database and send it to your development team! If you use the parameters --remove-deleted and --clean-cstm, it will be smaller too.

Post originally written by Emmanuel iNet.

 

Jun 14 2017 UPDATE

 

The latest versions of Sugar includes native support for Composer. You can customize the core ./composer.json to add and manage new 3rd party library dependencies. Customizing ./composer.json is only currently supported for Sugar On-Site. This method of using Composer is preferred over the one described by Emmanuel in this blog post due to the risk of conflicting dependencies through the use of two Composer projects within the same application.

 

Please view our Composer documentation in the Developer Guide for more details!

Here is a guest post from Emmanuel Dyan from iNET Process, an Elite SugarCRM Partner, and an active Sugar Developer community member as well as an open source advocate.Are you interested in posting on the Sugar Developer Blog? Contact developers@sugarcrm.com with your idea.

 

Managing libraries and dependencies within Sugar

 

Sugar Partners sometimes need to share libraries between various Sugar development projects. It is also required that we use the custom directory structure for Sugar customization. Sometimes developers will just copy and paste libraries or other dependencies into the custom directory that they need to use for each of their Sugar projects.  But there are better ways to manage your code's dependencies.Composer is the best way to manage your software dependencies for PHP, so much so that it is de facto standard that PHP developers are adopting (including the team at SugarCRM). In this blog post, we will explore how you can use Composer to easily manage libraries used with Sugar development.

 

If you haven't used Composer before, then check out Composer's Getting Started guide for installation instructions.

 

Installing libraries using Composer

 

Let us suppose we have a project where we need to read YAML files. The best component to do with PHP is symfony/yaml. So, instead of downloading it and unzipping it into any directory, we can use Composer by running :

$ cd custom/

 

$ composer require symfony/yaml

 

This will generate a composer.json file that contains our new dependency and download the symfony/yaml library into the vendor directory.  However, we also need to make some additional edits to this file.

 

Here is an example of a complete config file that we can use with Sugar.custom/composer.json

{ 

 

   "require":{

 

      "symfony/yaml":"^2.8"

 

   },

 

   "require-dev":{

 

      "inetprocess/libsugarcrm":"^1-beta"

 

   },

 

   "autoload":{

 

      "psr-0":{

 

         "":"include/lib/"

 

      }

 

   },

 

   "autoload-dev":{

 

      "psr-4":{

 

         "Inet\\SugarCRM\\Tests\\":"vendor/inetprocess/libsugarcrm/tests/"

 

      }

 

   },

 

   "config":{

 

      "platform":{

 

         "php":"5.4"

 

      }

 

   }

 

}

 

Importantly, using the config.platform.php setting above allows us to ensure everything downloaded is compatible with PHP 5.4 as required by the Sugar version we are using.

 

Since we want to follow PHP best practices, we can also add a folder to the autoloader to load our own PSR compliant classes and methods.

 

For example, we may create code with the Inet\MyLib namespace that we store at custom/include/lib/Inet/MyLib.

 

Also in the above example, we have included a dev dependency for inetprocess/libsugarcrm. This is an open source library that is helpful for writing Sugar unit tests for our customizations.

 

Adding Composer's Autoloader using Utils Extension

 

Finally, we'll ask Sugar to use Composer's autoloader by using Sugar's Utils Extension to include an additional autoloader.custom/Extension/application/Ext/Utils/composer.php

<?php

 

require_once(__DIR__ . '/../../../vendor/autoload.php');

 

With this in place we can share everything between modules!

 

Using sugarcli to quickly set up Composer

 

If you want to automatically setup Composer for your Sugar instance, just use ... sugarcli!

 

Follow instructions on Github to download it, install it, and launch:

./sugarcli.phar code:setupcomposer --path <sugarcrm_path> --do

 

# Or, if it's in the path: sugarcli code:setupcomposer --path <sugarcrm_path> --do

 

That will check if the file exist, complete the installation or create both the files. It can even reinstall Composer if you want to start from scratch.

Post originally written by tshubbard.

 

In our previous "Hello World" dashlet post, we established what a minimal dashlet entailed.  In these next post, we'll be building on those skills to create a more useful dashlet that takes advantage of Sugar 7 List Views.  We will be creating a dashlet for Cases that binds to the list's Collection and sums the number of Cases by their status.  So if the Cases list contains 5 records, and 3 of those are in "New" state and 2 are in "Closed" state then we want our dashlet to display "New: 3" and "Closed: 2".  To the code!

 

File Structure

 

Again, using what we learned in the previous post, we're going to create a folder in custom/clients/base/views/ called "case-count-by-status". Inside that folder you should create 3 files:

  • case-count-by-status.php
  • case-count-by-status.js
  • case-count-by-status.hbs

 

You should have something that looks like the following screenshot:

 

While technically optional, we will also utilize the Language extension in order to provide multilingual support for our example dashlet.  This extension file will be located at custom/Extension/application/Ext/Language/en_us.case-count-by-status.php.

 

Dashlet Metadata (.php file)

 

Dashlet metadata is going to look almost identical to our previous "Hello World" dashlet. We're not doing anything too fancy here, so everything should look basically the same.

 

case-count-by-status.php

<?php

/**
* Metadata for the Case Count by Status example dashlet view
*
* This dashlet is only allowed to appear on the Case module's list view
* which is also known as the 'records' layout.
*/
$viewdefs['base']['view']['case-count-by-status'] = array(
'dashlets' => array(
array(
//Display label for this dashlet
'label' => 'LBL_CASE_COUNT_BY_STATUS',
//Description label for this Dashlet
'description' => 'LBL_CASE_COUNT_BY_STATUS_DESCRIPTION',
'config' => array(
            ),
'preview' => array(
            ),
//Filter array decides where this dashlet is allowed to appear
'filter' => array(
//Modules where this dashlet can appear
'module' => array(
'Cases',
                ),
//Views where this dashlet can appear
'view' => array(
'records',
                )
            )
        ),
    ),
);

 

Dashlet Metadata Filter Options

 

Currently there are two main dashlet filter keys that you'll see in the codebase; "module" and "view".  Across of these filter keys, the main thing to remember is that not specifying a filter at all means that your dashlet will be available in all views of all modules. You only need to add filters if you desire to restrict your dashlet to a specific module or view.  Let's look at the filter keys in more detail.

Specifying a filter means your dashlet will be restricted to specified modules and views.  Not specifying a filter means your dashlet will be available in all modules and views.

 

"module"

 

The module filter lets you add an array of modules where your dashlet can appear. If you wanted your dashlet to appear in the list of available dashlets for only the Accounts, Cases, and Contacts modules then your module filter would look like the following.

 'filter' => array(

 

     'module' => array(

 

         'Cases',

 

         'Accounts',

 

         'Contacts',

 

     ),

 

)

 

"view"

 

The view filter lets you add an array of views to limit on which views your dashlet can appear. If you wanted your dashlet to appear only on the Record view, your view filter would look like the following.

 'filter' => array(

 

     'view' => array(

 

         'record',

 

     ),

 

)

 

Currently, there are two possible values for the view filter. The List View is indicated by using "records".  The Record View is indicated by using "record".

 

 

Dashlet Controller (.js file)

 

Enough metadata nonsense, now for the fun stuff!  Here is the JavaScript controller for a Case Count By Status dashlet.

 

case-count-by-status.js

/**
* Case Count by Status example dashlet controller
*
* Controller logic watches the current collection on display and updates the
* dashlet automatically whenever the current collection changes.
*
* This is a simple example of a dashlet for List Views in Sugar 7.x.
*
**/
({
//This view uses the essential Dashlet plug-in
    plugins: ['Dashlet'],

/**
     * Values is used by the template to display the statuses and counts.  Backs our Handlebars template.
*/
    values: undefined,

/**
     * Keeps track of how many cases in total we're displaying.  Also used in our Handlebars template.
*/
    totalCases: undefined,

/**
     * Keeps a map of status types by model ID as the key.
*/
    modelsMap: undefined,

/**
     * @inheritdoc
*/
initialize: function(options) {
// call the parent's (View's) initialize function
// passing options as an array
this._super('initialize', [options]);

// initialize vars
this.modelsMap = {};
this.totalCases = 0;
this.values = {};
    },

/**
     * @inheritdoc
*/
bindDataChange: function() {
var ctx = this.context,
            collection = ctx.get("collection");
if(_.isEmpty(collection)){  //Collection will be empty in "preview" mode
return;
        }

//Listening to 'reset' events on the collection
collection.on('reset', function(collection) {
// Ensure that collection exists, has models, then parse out models for display
if(collection && collection.length) {
this._parseModels(collection.models, false);
            }
        }, this);

//Listening to 'add' and 'remove' events on the collection
collection.on('add remove', function(model, collection, options)  {
// The Backbone's options argument for 'add' and 'remove' events are different
// if options.removed doesn't exist, then we will know this is a 'remove' event
if (_.isUndefined(options.remove)) {
options.remove = true;
            }

// Backbone passes add/remove options as an event param, so we can tell
// if this was the add or remove event and pass it to parseModels
this._parseModels([model], options.remove);
        }, this);

if(collection.models && _.isEmpty(this.modelsMap)) {
// manually cause a parsing of the models
// this covers the scenario when a user creates a new record
this._parseModels(collection.models, true);
        }
    },

/**
     * Recalculates values used in the template from modelsMap
     *
     * @private
*/
_recalcValues: function() {
// reset values
this.values = {};
this.totalCases = 0;

_.each(_.values(this.modelsMap), function(status) {
this.totalCases++;
// check to see if we've already set a value
// for this status
if (this.values[status]) {
// status is already set so just increment
this.values[status].count++;
            } else {
// add a new entry on the values object
// with status as the key and an Object
// with name and count for our template
this.values[status] = {
                    name: status,
                    count: 1
                };
            }
        }, this);
    },

/**
     * Takes an array of models and parses them into modelsMap then (re)counts the values in this map
     *
     * @param {Array} models The Array of models to parse
     * @param {Boolean} remove If the models passed in should be removed or not
     * @private
*/
_parseModels: function(models, remove) {
var id,
            status;

_.each(models, function(model) {
// get the case id & status
            id = model.get('id');
            status = model.get('status');

if (remove && this.modelsMap[id]) {
// if the function was called
// to remove models, delete the id/value
delete this.modelsMap[id];
            } else {
// otherwise, add the id and status
// to modelsMap
this.modelsMap[id] = status;
            }
        }, this);

// now that we've updated the modelsMap,
// recalculate the this.values object for rendering
this._recalcValues();

// double-check that the view has not been disposed
// if not, then re-render the dashlet
if (!this.disposed) {
this.render();
        }
    }
})

On a List View, this.context points to the BeanCollection.  this.context.parent points to a parent model (when it exists, such as on a dashlet preview).

 

Dashlet Template (.hbs file)

 

Again, with a Dashlet you are free to format the display any way you like.  However, we do recommend leveraging Sugar's Styleguide so that your dashlet appears like a seamless extension of the Sugar user interface.  It is a great reference for you to leverage that allows your dashlets to appear as just another seamless part of the Sugar 7 application.

 

In this example, we leveraged some dashlet design patterns and CSS pulled directly from the Sugar 7 Styleguide.

 



The Sugar Styleguide describes how Sugar 7's CSS works and the design patterns used in common components such as Dashlets.  The Styleguide is how you build seamless UI for Sugar 7.x.

 

As a Sugar Admin, navigate to Styleguide > Core Elements > Dashboards > Dashlets to view the Dashlets style guide.  Specifically, we borrowed CSS from the "Summary" dashlet example listed in the Styleguide.

 



 

Here is our Handlebars template we put together leveraging this "Summary" pattern.

 

case-count-by-status.hbs

{{!
Case Count by Status example dashlet Handlebars template

We are reusing styling from the Sugar 7 Styleguide for our example dashlet.

Here we are borrowing CSS used in our Forecast Details dashlet which is suitable for display any set of name value pairs.
}}
<div class="forecast-details">
{{#each values}}
        <div class="row-fluid">
            <span class="span6">
{{name}}
            </span>
            <span class="span6 tright">
{{count}}
            </span>
        </div>
{{/each}}
    <div class="row-fluid">
        <strong class="span6">
{{! 'str' is the Sugar 7 Handlebars helper that translates a label into a localized language string}}
{{str "LBL_CASE_COUNT_BY_STATUS_TOTAL"}}
        </strong>
        <strong class="span6 tright">
{{totalCases}}
        </strong>
    </div>
</div>

 

Language Extension (.php file)

 

Finally, in the above Handlebars template and in the dashlet metadata file we have defined some display labels that need to be translated into human readable strings.  To accomplish this, we have added a language extension for these new labels that we have introduced.

It is a development best practice to use labels for strings so that your user interface can be translated and supported in multiple languages

 

en_us.case-count-by-status.php

<?php
// This file will provide English strings for our new labels.
// Additional extensions could be created so that our dashlet supports other languages.
$app_strings['LBL_CASE_COUNT_BY_STATUS'] = 'Case Count By Status';
$app_strings['LBL_CASE_COUNT_BY_STATUS_DESCRIPTION'] = 'Shows the number of Cases on the Cases List view by status.';
$app_strings['LBL_CASE_COUNT_BY_STATUS_TOTAL'] = 'Total Cases:';

 

The "en_us" at the beginning of this filename is significant.  It represents the user locale where these strings are used.  If you wanted to created translations for other locales, then this value would be different.  For example, French language extensions must start with "fr_FR".

 

Wrapping Up

 

So now we've got a working example dashlet that takes advantage of a module's List View.  Remember to run a Quick Repair and Rebuild and then navigate to your Cases module and add the new dashlet.  It should look something like what you see below.

 



Post originally written by tshubbard.

 

For Sugar Developers, the Dashboard and Dashlet framework provided in Sugar 7 really helps you to build slick reusable Dashlets that can be targeted for certain parts of the app or made globally available anywhere in the app.  They're also really easy to use which is something we are gonna highlight today.

 

For a more detailed look at how Sugar 7 Dashlets work then you should read up on the topic in the Sugar Developer Guide.  The focus on this post is to highlight the most basic elements necessary that we can then build upon in later posts.

 

A quick reminder

 

Just some quick advice to anyone about to embark on the Dashlet development journey.  We recently featured a post on a common Sugar 7.x dashlet gotcha.  Basically, dashlet metadata gets copied into database when you add a dashlet to a dashboard.  You'll need to delete the dashlet or the dashboard and add it all over again whenever you modify dashlet metadata.  We are working on making this seamless in future but this is something you should remain aware of when building dashlets.

 

Creating a Dashlet Summarized

 

Dashlets are simply Sugar 7 Views that have some extra features added via the 'Dashlet' plug-in and an additional metadata requirement. This tutorial will be brief because creating a basic dashlet is incredibly easy.  As with any Sugar 7 view, we will need three files: a JS controller, a PHP metadata file, and a Handlebars template for the presentation.

 

Shall we begin?

 

Step 1: Create a 'hello-world' directory

 

In your Sugar 7 instance, create a new directory called custom/clients/base/views/hello-world and create three files called hello-word.js, hello-world.php, and hello-world.hbs inside.  This is just like any other Sidecar view in Sugar 7.

Sugar 7 Dashlets are just like any other Sidecar view


 

Step 2: Implement the Dashlet Controller (JavaScript)

 

To make this "hello-world" view a dashlet, all we need to do is add one line to the view's JS Controller.  This adds the 'Dashlet' Sugar 7 plug-in to your view.  This applies a bunch of changes in behavior to your view that gives it a Dashlet nature.  Plug-ins are essentially Sugar 7's implementation of the Mixin JavaScript design pattern.  You can examine the Dashlet plug-in implementation under include/javascript/sugar7/plugins/Dashlet.js if you are curious but for the most part you don't need to worry about the details.  Just know that all dashlets needs to include the 'Dashlet' plug-in in their controller.

At a minimum, Dashlet controllers must include the 'Dashlet' plug-in.
hello-world.js

 

hello-world.js

({
    plugins: ['Dashlet']
})

 

Step 3: Implement the Dashlet Metadata (PHP)

 

Dashlet metadata is also fairly straightforward. Make sure your metadata is wrapped in an array labeled 'dashlets' and it should have the following params:

  • label - A String label, preferably a language string key "LBL_HELLO_WORLD_TITLE" or something similar that you've included in a custom language file
  • description - A String description, again, preferably a language string key
  • config - an array of config params (if applicable) that allows users to configure the dashlet before it's added to a dashboard
  • preview - an array of preview params (if applicable) to be passed when your dashlet is "previewed" when being added to a dashboard
  • filter - an array of filter params (if applicable) for making this dashlet only show up on a specific view or module
At a minimum, all Dashlet metadata must include a 'dashlets' array that defines a 'label' and 'description'.
hello-world.php

 

hello-world.php

<?php

$viewdefs['base']['view']['hello-world'] = array(
'dashlets' => array(
array(
'label' => 'Hello World!',
'description' => 'Hello World is a basic dashlet for the SugarCRM Cookbook tutorial.',
'config' => array(
            ),
'preview' => array(
            ),
'filter' => array(
            )
        ),
    ),
);

 

Step 4: Implement the Dashlet Template (Handlebars)

 

The template can be as basic as you want it to be.  There is no requirement for the content you put into the template.

There is no minimum requirement for Dashlet templates.  You are in full control of the presentation.
hello-world.hbs

 

hello-world.hbs

<p>
    Hello World!
</p>

 

Step 5: Run Quick Repair and Rebuild

 

So that is all the programming we need to do. With those three files in place, we just need to rebuild the cache so that Sugar 7 picks up our new Dashlet and makes it available to end users.

 

The simplest way to do this is to run Quick Repair and Rebuild or by manually removing the contents of your cache/ directory and reloading the Sugar 7 web app which will then rebuild the file cache automatically.

 

Step 6:  Add your Dashlet to a Dashboard

 

If you login now as any user and try to add a dashlet to any dashboard, you should see your "Hello World" dashlet listed.  This dashlet is an option within all dashboards because we did not specify and dashlet filter criteria in Step 3 when we defined the metadata.

 



 

After adding it to a dashboard and saving it, you should see your dashlet appear just like the one below.

 



Post originally written by Joo Lee.

 

Sugar 7 allows you to define your own view metadata for creating new custom layouts and views.  In these custom view definitions, the correct use of the type and name attributes is essential when you want to reuse metadata, controllers, or templates from other Sidecar components. But what are type and name?  How are they used? In this post, we hope to provide you a better understanding of these attributes and how they are used by Sidecar when your components get created.  Most importantly, this post will serve as a guide for the correct use of type and name in your Sugar 7 view metadata that will maximize your ability to reuse.

 

Read more below!

 

Review of Basic Components

 

There are three basic Sidecar components in Sugar 7 user interfaces: layout, view, and field. A layout contains views and other layouts. It is primarily used to lay out various components of a page. For a simple application, a layout should be lightweight. It should create and define other components and nothing else. A view is where most of the work happens. It is also where fields are created and are defined.

 

Components consist of three parts: definition, controller, and templates. Component definition specifies the components to be created and its customizable attributes. Controller adds behavior and logic to components via JavaScript. Templates provide HTML to the component through Handlebars template engine.

 

For more information, you can read about layouts, views, and fields in the Sugar 7.5 Developer Guide.

 

Component Definition

 

There are two ways to create and define your components. The first is to reference an existing component definition. The second is to specify it directly in your component.

 

Layout example

 

foo.php

$viewdefs['base']['layout']['foo'] = array(
   'components' => array(
      array(
         'layout' => 'header', //reference another definition
      ),
      array(
         'layout' => array( //directly include definition
            'components' => array(
               array(
                  'view' => 'title', //reference another definition
               ),
               array(
                  'view' => 'content', //reference another definition
               ),
            ),
            'type' => 'base',
            'name' => 'my-custom-main',
         ),
      ),
   ),
   'type' => 'foo',
   'name' => 'my-custom-page'
),

In the above example, the layout definition tells the application that this is a “foo” layout and the name is “my-custom-page.” It directly defines “header” and “base” layouts. For the “header” layout, the application gets the definition from “header” layout to create its components. The “base” layout, which is defined directly, has two views “title” and “content.” These two views are referenced like the “header” layout. So, the application will retrieve these definitions from the two views to create them.

 

View example

 

foo.php

$viewdefs['base']['view']['foo'] = array(
   'buttons' => array( //directly include definition
      array(
         'type' => 'button',
         'name' => 'cancel',
      ),
      array(
         'type' => 'button',
         'name' => 'save',
      ),
   ),
),

 

In the above example, the view definition indicates that this is a "foo" view, and it has two buttons: cancel and save.  The two buttons, which are defined directly, are fields with type "button" with one named "cancel" and the other "save."

 

Component Controllers

 

Both the type and name attributes are used to determine which JavaScript controller is used for a particular component.

For all component types, when the type attribute is set then it is used to select the JavaScript controller to initialize.  For views and layouts, if type attribute does not exist then the name attribute is then used. For fields, only the type attribute is examined.

 

Layout example

 

foo.php

$viewdefs['base']['layout']['foo'] = array(
   'components' => array(),
   'type' => 'foo',
),

 

In the above example, since "foo" layout has type attribute specified, the application will use the "foo" layout JavaScript controller.

 

View example

 

foo.php

$viewdefs['base']['view']['foo'] = array(
   'buttons' => array(
      array(
         'type' => 'button',
         'name' => 'save',
      ),
   ),
   'name' => 'bar',
),

 

In the above example, "foo" view does not have type attribute. So, it will look at the name attribute to get the JavaScript controller. For the button inside "foo" view, JavaScript controller for button field is used.

 

Component Templates

 

The type and name attributes also affect which HTML templates are used.

For both layouts and views, the application uses the name attribute to select the HTML template. If that template does not exist, it uses the type attribute. For views only, the template attribute can be used to specify which template to use. For fields, the template is determined by the type attribute but also a variety of other factors such as user ACL, current action, and the name of the parent view.

 

Layout example

 

foo.php

$viewdefs['base']['layout']['foo'] = array(
   'components' => array(),
   'type' => 'foo',
   'name' => 'bar',
),

 

In the above example, "foo" layout will use "bar" template in "foo" layout. If the template does not exist, it will use "foo" template.

 

View example

 

foo.php

$viewdefs['base']['view']['foo'] = array(
   'buttons' => array(),
   'type' => 'foo',
   'name' => 'bar',
   'template' => 'detail',
),

 

In the above example, "foo" view will use "detail" template in "foo" view. If that template does not exist, "bar" template will be used. If "bar" template does not exist, it will use "foo" template.

 

Best Practices

  • For clarity, specify both type and name attributes when specifying your view definitions.
  • When referencing another view or layout in your definition, use the name attribute only.

  • For views and layouts, type and name attributes should have the same value in most cases. In cases where type and name attributes need to differ, please note that the component will use the name attribute to determine its metadata, the type attribute to determine its controller and the name attribute to determine its template.

Post originally written by Jesse.

 

If you want to use namespaces following the PSR-0 standard for your custom code, it is now possible with the SugarAutoLoader in sugar7. Now you don't have to insert require statements all over the place.
require_once('include/utils/autoloader.php');

 

SugarAutoLoader::addNamespace('Fbsg\\', 'custom/include/Fbsg');

 

Sugar's autoloader will now map requests for the class Fbsg\Example\Test to the file "custom/include/Fbsg/Example/Test.php".

 

Post From Faye Business Systems Group Director of Development Matthew Strickland

 

Contact Jesse Heller

Post originally written by Jesse.

 

When you work with something everyday, eliminating tedious and repetitive tasks not only improves your productivity but also helps your sanity. With Sugar, there are certain things we as developers are constantly doing; in some cases, over and over again. How many times have you run quick repair and rebuild?

 

Luckily, we program things. We write scripts that make our lives in that moment a tad bit easier. But it's easy to lose track of these things; we might even write the same tool over and over again.

 

In this series of posts, we're going to build a command line utility tool whose only purpose is to make life more beautiful. If you've ever used the laravel framework, you are familiar with the "artisan" tool which is built with the symfony console component. We are going to do the same thing.

 

The general idea here is to be able to call commands like these:

 

$ sugarcli admin:repair

 

$ sugarcli manifest:validate

 

Let's get started.

 

We are going to be using symfony console composer, so if you aren't familiar be sure to read-up on it. The first thing, we need to do is pull in our symfony console dependency in composer.json.

{

 

    "require": {

 

        "symfony/console": "v2.4.4"

 

    },

 

    "autoload": {

 

        "psr-4": {

 

            "Fbsg\\": "src"

 

        }

 

    }

 

}

$ composer install

 

Notice that we are using psr-4 for are auto-loading our classes, as we will be using psr compliant namespaces.

 

Now that we have our dependencies installed to the vendor folder, we are ready to start.

 

In the next post, we'll be building the quick repair/rebuild command.

 

You can view the github repository here.

 

Post From Faye Business Systems Group Director of Development Matthew Strickland

 

Contact Jesse Heller

Post originally written by John Mertic.

 

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:

 

pm-record-logic.js

initialize: function(options) {

this._super("initialize", arguments);

this.context.on('button:pm_record_reports:click', this.pmRecordReports, this);

},

 

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

 

record.php

<?php 

require_once('clients/base/views/record/record.php');

foreach($viewdefs['base']['view']['record']['buttons'] as &$set){
if( $set['type'] == 'actiondropdown' && $set['name'] == 'main_dropdown'){
$set['buttons'][] = array(
'type' => 'rowaction',
'event' => 'button:pm_record_reports:click',
'name' => 'pm_record_reports',
'label' => 'PM Record Reports',
'acl_action' => 'view',
            );
    }
}

 

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

 

record.php

<?php 
require_once('clients/base/layouts/record/record.php');

if(isset($viewdefs['base']['layout']['record']['components'])){
foreach($viewdefs['base']['layout']['record']['components'] as &$subArray){
if(isset($subArray['layout']['components'])){
foreach($subArray['layout']['components'] as &$subsubArray){
if(isset($subsubArray['layout']['components'])){
$subsubArray['layout']['components'][] = array(
'layout' => 'pm-record-logic',
                        );
                }
break;
            }
        }
break;
    }
}

 

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

 

pm-record-logic.php

<?php

$viewdefs['base']['layout']['pm-record-logic'] = array(
'type' => 'simple',
'components' => array(
array(
'view' => 'pm-record-logic',
            ),
        ),
    );


 

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

 

pm-record-logic.hbs

<div id="pm-record-logic">

</div>

 

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

 

pm-record-logic.js

({
    className: 'pm-record-logic tcenter',
initialize: function(options) {
this._super("initialize", arguments);
this.context.on('button:pm_record_reports:click', this.pmRecordReports, this);
    },
pmRecordReports: function() {
var object_id = this.model.get('id');
var object_name = this.model.attributes._module;
app.api.call('GET', app.api.buildURL('PM_ProcessManager/' + object_id + '/record_reports/' + object_name), null, {
success: function(data) {
app.drawer.open({
                    layout:'pm-record-reports',
                    context:{
                        PM_Data: data
                    }
                });
            },
error: function(error) {
app.alert.show("server-error", {
                    level: 'error',
                    messages: 'ERR_GENERIC_SERVER_ERROR',
                    autoCLose: false
                });
app.error.handleHttpError(error);
            }
        });
    },
})

 

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.

Post originally written by John Mertic.

 

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'...

 

gistfile1.json

{
"grant_type":"password",
"client_id":"sugar",
"client_secret":"",
"username":"admin",
"password":"password",
"platform":"myspecialapp" // change 'myspecialapp' to whatever you want to refer to your app as
}

Post originally written by John Mertic.

 

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'...

 

 

 

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

Post originally written by Jim Bartek.

 

UPDATE October 3rd 2017

 

Removed references to compileSql() function which was deprecated and removed in Sugar 7.9.

 

You have found yourself in a bind, and you need to query the database directly. There is no other recourse than to write a query to get the data you need. This cookbook entry is going to give you some examples on how to use our new SugarQuery API instead of direct SQL.

 

1. What is SugarQuery?

 

SugarQuery is a SQL query builder for retrieving data directly from the database.  It is used extensively within the core of the application.  For instance, the FilterAPI uses it.

 

It uses a bean, the beans relationships, and visibility models to build a SQL query that can be used to retrieve data.

 

2. The Basics

 

SugarQuery has a very simple interface for building queries.

 

The basic methods you will need to create a query are:

  • select($fields) - accepts an array of fields you would like to select
  • from($bean) - validates the query against a SugarBean at generation
  • where() - creates an AND where object
  • orWhere() - creates an OR where Object
  • join($link) - accepts a link from the from bean to create the correct join(s)
  • orderBy($field, $direction) - accepts a field and a direction to sort upon
  • groupBy($field) - accepts a field to group by
  • having($condition) - accepts a condition (see below)

 

There is also conditions that can be used when building out your query.  This conditions can be used with the where and having.

 

To execute a query you call execute() on the object.  If you would like to see the sql it generated you can call $query->compile()->getSQL() and $query>compile()->getParameters().

 

The execute() method by default returns an array of the records selected. You may also choose to return the db result object execute('db') or return as json execute('json').

 

Lets try a simple example of selecting all Accounts that have an industry of 'Media'.

 

sugarquery-accounts.php

<?php
$query = new SugarQuery();
$query->select(array('id','name'));
$query->from(BeanFactory::getBean('Accounts'));
$query->where()->equals('industry','Media');
$results = $query->execute();

Now lets roll through some examples.

 

3. Joins

 

Let's now get all the contacts on these media accounts.

 

sugarquery-acconts-contacts.php

<?php
$query = new SugarQuery();
$query->from(BeanFactory::getBean('Accounts'));
$contacts = $query->join('contacts')->joinName();
$query->select(array("$contacts.full_name"));
$query->where()->equals('industry','Media');
$results = $query->execute();

 

4. N-Deep Joins

 

Lets say you need to get all media industry accounts, contacts, that are attached to closed opportunities.  Why?  Because you can!

 

sugarquery-n-deep-joins.php

<?php
$query = new SugarQuery();
$query->from(BeanFactory::getBean('Accounts'));
$contacts = $query->join('contacts')->joinName();
$opp = $query->join('opportunities', array('relatedJoin' => $contacts))->joinName();
$query->select(array("$contacts.full_name", "$opp.name"));
$query->where()->equals('industry','media');
$query->where()->equals("$opp.sales_stage", 'closed');
$results = $query->execute();

As you can see, you can prefix a field with the link name, and when the SQL is generated it will replace those with the system generated aliases.

 

5. But I want my own aliases!

 

So you want to alias things yourself?  Alright!  You can set aliases everywhere!

 

sugarquery-aliasing.php

<?php
$query = new SugarQuery();
$query->from(BeanFactory::getBean('Accounts'));
$query->join('contacts', array('alias' => 'industryContacts'));
$query->join('opportunities', array('relatedJoin' => 'industryContacts', 'alias' => 'contactsOpportunities'));
$query->select(array(array('industryAccounts.first_name', 'fname'), array('industryContacts.last_name', 'lname'), 'contactsOpportunities.name'));
$query->where()->equals('industryAccounts.industry','Media');
$query->where()->equals('contactsOpportunities.sales_stage', 'closed');
$results = $query->execute();


6. Order the madness

 

Now you have this data, but you want to sort it, make it make some sense.  Thats EASY!

 

sugarquery-orderby.php

<?php
$query = new SugarQuery();
$query->from(BeanFactory::getBean('Accounts'));
$query->join('contacts', array('alias' => 'industryContacts'));
$query->join('opportunities', array('relatedJoin' => 'industryContacts', 'alias' => 'contactsOpportunities'));
$query->select(array(array('industryContacts.first_name', 'fname'), array('industryContacts.last_name', 'lname'), 'contactsOpportunities.name'));
$query->where()->equals('industryAccounts.industry','Media');
$query->where()->equals('contactsOpportunities.sales_stage', 'closed');
$query->orderBy('industryContacts.name', 'DESC');
$results = $query->execute();

 

7. Groups!

 

Time to group by!

 

sugarquery-groupby.php

<?php
$query = new SugarQuery();
$query->from(BeanFactory::getBean('Accounts'));
$query->join('contacts', array('alias' => 'industryContacts'));
$query->join('opportunities', array('relatedJoin' => 'industryContacts', 'alias' => 'contactsOpportunities'));
$query->select()->setCountQuery();
$query->where()->equals('contactsOpportunities.sales_stage', 'closed');
$query->groupBy('accounts.industry');
$results = $query->execute();

 

8. Having!

 

For a having we need to setup a condition object.  To do this, instantiate the SugarQuery_Builder_Condition object and set the operator, field, and values.

 

sugarquery-having.php

<?php
$query = new SugarQuery();
$query->from(BeanFactory::getBean('Accounts'));
$query->join('contacts', array('alias' => 'industryContacts'));
$query->join('opportunities', array('relatedJoin' => 'industryContacts', 'alias' => 'contactsOpportunities'));
$query->select()->setCountQuery();
$query->where()->equals('contactsOpportunities.sales_stage', 'closed');
$havingCondition = new SugarQuery_Builder_Condition($query);
$havingCondition->setField('contactsOpportunities.amount')->setOperator('>')->setValues('1000');
$query->having($havingCondition);
$results = $query->execute();


Conclusion

 

SugarQuery is your one stop shop for getting your data out of the database.  In part two we can examine more advanced options.