Matt Marum

Using server side changes to customize SugarCRM Mobile

Blog Post created by Matt Marum Employee on Jun 11, 2015
If you were at UnCon in April 2015 then you know that one of our hottest topics was plans for a SugarCRM Mobile SDK.  Since mobile devices and mobile use cases are such a key part of what Customer Relationship Management and Customer Experience is today and will be in the future, we are planning to enhance the SugarCRM Mobile development platform available to Sugar Developers.  You can follow the blog here for updates on that as they become available in the future.

 

However, there are already many different ways that developers can customize the behavior of the existing SugarCRM Mobile application today.  That will be the focus of today's post.  In fact, you can drastically customize the behavior of the SugarCRM Mobile application without the hassle of having to distribute any custom code or apps to end-user Mobile devices.  All these techniques listed below involve only Sugar 7 server side code and metadata customizations.

 

Metadata based Customizations

 

As every Sugar Developer should be well aware, Sugar metadata serves as the architectural backbone of the Sugar 7 platform.  All Sugar 7 clients, including the SugarCRM Mobile application, are driven via Sugar metadata which means changes in Sugar metadata can already be used to reflect changes in appearance and behavior of the Mobile client without having to write a line of JavaScript.

 

Mobile Layouts & Sugar Logic

Sugar Logic allows you to utilize expressions and actions to establish dependencies between fields within a Sugar module.  It is already a best practice to utilize Sugar Logic where possible since it is applied consistently across the application both client and server-side.  So always remember that Sugar Logic works in SugarCRM Mobile too.

 

For example, if you want to display custom information or messages to a user on Mobile, you could add a read-only dependent field to Mobile Layouts to deliver it.

 

Try this example, all you need to do is create a calculated field in Sugar Studio and configure the formula as seen below.

 

sugarfield_next_step_c.php

<?php
// created: 2015-06-05 10:27:18
$dictionary['Lead']['fields']['next_step_c']['duplicate_merge_dom_value']=0;
$dictionary['Lead']['fields']['next_step_c']['labelValue']='Next Step';
$dictionary['Lead']['fields']['next_step_c']['full_text_search']=array (
'boost' => '0',
'enabled' => false,
);
$dictionary['Lead']['fields']['next_step_c']['calculated']='1';
$dictionary['Lead']['fields']['next_step_c']['formula']='ifElse(equal(count($tasks),0),"You must create a follow up Task for this Lead!",concat("Complete ",toString(related($tasks,"name"))))';
$dictionary['Lead']['fields']['next_step_c']['enforced']='1';
$dictionary['Lead']['fields']['next_step_c']['dependency']='';

 



 

API based Customizations

 

Similar to Sugar metadata, the Sugar 7 REST API serves as the transport backbone for the Sugar 7 platform.  All Sugar 7 clients, including the SugarCRM Mobile application, utilize the same APIs to communicate with Sugar server.  This makes the APIs the ideal critical point to intercept messages that come from the Mobile client.

 

API Logic Hooks

 

Most Sugar Developers are familiar with the Module Logic Hooks that are available in the system.  For example, whenever a record gets saved to the server in Sugar the before_save and after_save logic hooks are triggered.  It doesn't matter that these requests originated from the Mobile client, the base Sugar 7 web client, the Sugar portal client, or some other web services integration.

 

However, each user session (and therefore each API request) does have a platform identifier associated with it.  This is gets associated with the session when the user logged in.  For requests coming from the SugarCRM Mobile application, this platform identifier is always "mobile".  With this in mind, it's possible to intercept each API call from a Mobile client session via one or more API Logic Hooks in order to do something special in those cases.  In the example below, we just write information about the request to the sugarcrm.log file.

 

To try this example, copy the following two files to custom/MobileApiLogicHook.php and  custom/Extension/application/Ext/LogicHooks/after_routing_mobile.php.

 

after_routing_mobile.php

<?php

$hook_version = 1;
$hook_array = Array();

$hook_array['after_routing'] = Array();
$hook_array['after_routing'][] = Array(
//Processing index. For sorting the array.
1,

//Label. A string value to identify the hook.
'after_routing mobile logic hook',

//The PHP file where your class is located.
'custom/MobileApiLogicHook.php',

//The class the method is in.
'MobileApiLogicHook',

//The method to call.
'logMobileAfterRouting'
);

MobileApiLogicHook.php
<?php

if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

/**
* Example Mobile API Logic Hooks class
**/
class MobileApiLogicHook
{
/**
     * Logic hook function tied to 'after_routing' event
*/
function logMobileAfterRouting($event, $arguments)
    {
//Sugar Logger
global $log;
//If request came from a Mobile platform
if($arguments['api']->platform == "mobile"){
$path = $arguments['api']->getRequest()->getPath();
//Write request path to sugarcrm.log file
$log->fatal("Mobile request to " . json_encode($path));
        }

    }
}

Thu Jun 11 13:23:52 2015 [15726][-none-][FATAL] Mobile request to ["oauth2","token"]

 

Thu Jun 11 13:23:53 2015 [15726][1][FATAL] Mobile request to ["me"]

 

Thu Jun 11 13:23:53 2015 [15635][1][FATAL] Mobile request to ["metadata"]

 

Thu Jun 11 13:23:56 2015 [15635][1][FATAL] Mobile request to ["ping","whattimeisit"]

 

Thu Jun 11 13:23:56 2015 [15635][1][FATAL] Mobile request to ["ping"]

 

Thu Jun 11 13:23:57 2015 [15726][1][FATAL] Mobile request to ["Dashboards"]

 

Thu Jun 11 13:23:57 2015 [15635][1][FATAL] Mobile request to ["Users","filter","count"]

 

Thu Jun 11 13:23:57 2015 [15726][1][FATAL] Mobile request to ["bulk"]

 

Thu Jun 11 13:23:57 2015 [15726][1][FATAL] Mobile request to ["Dashboards","f3b37ac6-60f2-0606-18a0-55773d752273"]

 

Thu Jun 11 13:23:57 2015 [15726][1][FATAL] Mobile request to ["Dashboards","be9896e2-cddc-f88c-c161-55773d8013ed"]

 

...

 

Override API endpoints for Mobile platform

 

Or you can define a custom API endpoints for the Mobile platform to handle specific requests entirely differently for the Mobile clients than you would for other clients.  In the example below, we have modified the OAuth 2 API that is used for authentication to restrict mobile device access to Sugar 7.  This restriction could be based upon the role of the current user, whether Sugar 7 is being accessed via the native iOS, Android app or a mobile device's web browser.  It is even possible to check the kind of device being used (tablet, phone, or PC).

 

To try this example, add the following file to custom/clients/mobile/api/RestrictedOAuth2MobileApi.php

 

RestrictedOAuth2MobileApi.php

<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

require_once 'clients/mobile/api/OAuth2MobileApi.php';

/**
* Example of how to override the core OAuth2MobileApi class in order to alter SugarCRM Mobile authentication behavior
**/
class RestrictedOAuth2MobileApi extends OAuth2MobileApi {

/**
     *
     * By overriding the token function, we can show an example of how to restrict user access to Sugar 7 via Mobile clients/devices.
     *
     * @param ServiceBase $api The service api
     * @param array $args The arguments passed in to the function
     * @throws SugarApiExceptionNotAuthorized If user is not allowed or not using a supported mobile client
     * @return array Access token if login successful
*/
public function token(ServiceBase $api, array $args)
    {
global $current_user;

/** 
        *   $args['client_info'] contains information about the client being used
        *
        * For example,
        *       ['client_info']['app'] is an array of information about the SugarCRM Mobile app itself (app name, app version, if it's native or not, etc.)
        *       ['client_info']['browser'] is an array of information about the web browser being used (web kit enabled, user agent string, etc.)
        *       ['client_info']['device'] is an array of booleans ('desktop', 'phone', and 'tablet') for the type of device being used
        *     
        **/

// No tablets!  (for some reason.)
if($args['client_info']['device']['tablet']){
throw new SugarApiExceptionNotAuthorized();
        }

// continue to perform login as we normally would, we need to do this in order to collect $current_user id
$authData = parent::token($api, $args);

// This is a valid user, but we then need to check if they are on a Restricted role
$roles = ACLRole::getUserRoleNames($current_user->id);
// If user is in a Restricted role...
if (in_array('Restricted', $roles)) {
//Log user back out to cleanup session
parent::logout($api, $args);
//And throw Not Authorized exception.
throw new SugarApiExceptionNotAuthorized();
        }
return $authData;
    }


}

 



 

Metadata & API based Customizations

 

You can also combine the techniques described above to create more sophisticated SugarCRM Mobile customizations.

 

Lightweight Custom Modules

 

While SugarCRM Mobile does not yet support custom views, it does support the addition of new custom modules.  So if you already have an existing Custom Module that you want to display in the Mobile client then consider creating an additional simplified Custom Module that is a facade for your existing custom module or even one of Sugar 7's BWC modules.  You can then use a Sugar Job to synchronize key data into your mobile Custom Module.   Or (as seen below) you can modify the Mobile Module's API endpoints to pull data from other Modules or sources in real time.

 

To try this example, you will need to create a test_MobileContracts custom module using Module Builder, define the layouts, fields, metadata, etc, for it and then enable it for Mobile via the Admin panel.  Then add the following file to modules/test_MobileContracts/clients/mobile/api/MobileContractsMobileFilterApi.php

 

MobileContractsMobileFilterApi.php

<?php

require_once 'clients/base/api/FilterApi.php';

/**
* Class MobileContractsMobileFilterApi
*
* Tested with Sugar 7.6
*
* Example of how to build a simple proxy API that can be used to make API requests to one module pull data from another module.
* In this case, we can pull data from the Contracts module (which is not implemented in SugarCRM Mobile yet) via a
* proxy Custom Module called test_MobileContracts.  This is a way to satisfy a need for a limited view of Contracts
* data from within the SugarCRM Mobile app.
*
*/
class MobileContractsMobileFilterApi extends FilterApi
{
public function registerApiRest()
    {
//New API definition that the base Filter API endpoint for only test_MobileContracts module
return array(
'filterModuleAll' => array(
'reqType' => 'GET',
'path' => array('test_MobileContracts'),
// Not needed because we will hard code module argument below.
// 'pathVars' => array('module'),
'method' => 'myFilterList',
'jsonParams' => array('filter'),
'shortHelp' => 'Proxy API for retrieving all Contract records.',
'exceptions' => array(
// Thrown in filterList
'SugarApiExceptionInvalidParameter',
// Thrown in filterListSetup and parseArguments
'SugarApiExceptionNotAuthorized',
                ),
            ),
        );
    }

public function myFilterList(ServiceBase $api, array $args, $acl = 'list')
    {
//Hard code module redirect
$args['module'] = 'Contracts';

// We could transform the 'fields' argument here, but we could also assume that
// the field names being passed match those found in Contracts module

//Call normal filter API endpoint with our modified arguments
return $this->filterList($api, $args, $acl);
    }

}

 



Outcomes