Skip navigation
All Places > Developer > Blog > Authors Shijin Krishna

Developer

3 Posts authored by: Shijin Krishna

Here is another guest post from Shijin Krishna from BHEA Technologies.

 

When a user logs into Sugar 7, an OAuth access token (with a 1 hour timeout by default) and a refresh token (with a 2 week timeout by default) are returned. When the access token expires, Sugar will automatically retrieve another access token as long as the refresh token is valid. This allows a user to use a Sugar browser tab for days on end without having to log back in.

 

Automated notification requests are made to the server on the user's behalf at a default interval of every 5 minutes. These requests will allow the current session to remain active without actual user input. So tracking user activity by adjusting access token and refresh token expiry time or tracking network activity alone is not a good idea.

 

In this blog we are going to explore a way to track a user's true idle time based on actual user interface activity. For example, one or more of clicks, typing, mouse movements etc. To track a user's idle time we will use the JQuery IdleTimeout plugin.

 

This allow us to configure reasonable settings for the OAuth access token and refresh token to allow Lotus Notes, Outlook and other plugins to function for a longer period without needing to login again, while continuing to reliably enforce idle logout on the web client.

 

We will also learn to configure maximum idle time crossing the same will log the user out from Sugar automatically.

 

Step 1) Add JQuery IdleTimeout plug-in to Sugar JS

 

Create a new JS Grouping Extension file in the following path.custom/Extension/application/Ext/JSGroupings/idleTimer.php

 

 

 

<?php

 

// Copyright Shijin Krishna. This work is licensed under an Apache 2.0 license.

 

$js_groupings[] = $sugar_grp_sidecar = array_merge($sugar_grp_sidecar, array(

 

 

        'custom/include/javascript/jquery-idleTimeout.min.js' => 'include/javascript/sugar_sidecar.min.js',

 

        'custom/include/javascript/idleTimer.js' => 'include/javascript/sugar_sidecar.min.js',

 

    )

 

);

 

  • jquery-idleTimeout.min.js - Contains the source code for JQuery IdleTimeout plugin.
  • idleTimer.js - We will talk about this little later.

store.js is already included if using Sugar 7.9 or later! For older Sugar versions, you may need to download it from here and modify the example above to include it.

Add the IdleTimeout plug-in and store.js at following paths:custom/include/javascript/jquery-idleTimeout.min.jscustom/include/javascript/store.min.js

 

Step 2) Start the idle timer

 

We will start tracking users inactivity time once the app:sync:complete event is triggered. The JQuery Idle Timeout plugin comes with a set of configurable parameters which will allow us to define the maximum idle time, callback to execute when the idle time reaches the maximum limit, etc. Please click here to view more public configuration variables.custom/include/javascript/idleTimer.js

 

 

 

/**

 

* Idle time logout

 

* Copyright Shijin Krishna. This work is licensed under an Apache 2.0 license.

 

* Date 11/29/2016

 

*

 

* */

 

(function(app){

 

app.events.on('app:sync:complete',function(){

 

$(document).idleTimeout({

 

redirectUrl:'#logout', //redirect url

 

idleTimeLimit: app.config.max_idle_time || 600, // 'No activity' time limit in seconds. 600 = 10 Minutes

 

idleCheckHeartbeat: 10, // Frequency to check for idle timeouts in seconds

 

// optional custom callback to perform before logout

 

customCallback: function(){

 

app.logger.error("Logging out user after maximum idle time:" + app.config.max_idle_time); // this method will destroy user's session and log user out

 

// Due to bug with customCallbacks with idleTimeout jQuery plug-in,

 

// We must reload document to remove idleTimeout from page until user logs in again

 

window.location.reload();

 

},

 

enableDialog: false

 

});

 

});

 

})(SUGAR.App);

 

 

 

Step 3) Configuring the max idle time

 

By default our timer will consider ten minutes as the maximum idle time. But this can be configured by adding a new parameter 'max_idle_time' to the config_override.php file which is available under sugar root directory.config_override.php

 

 

 

<?php

 

// Copyright Shijin Krishna. This work is licensed under an Apache 2.0 license.

 

$sugar_config['additional_js_config']['max_idle_time'] = 1800;

 

 

 

Step 4) Rebuild Extensions & Configuration

 

Finally, you will need to run Quick Repair & Rebuild, Rebuild GS Grouping Files and Rebuild Config File in order to build your new extensions and configuration. You will also need to do a hard refresh of the browser page in order to load the updated JavaScript files.

 


Here is another guest post from Shijin Krishna from BHEA, an Elite SugarCRM Partner!

 

Notes is a common module used to track ad hoc information within Sugar. Sugar Enterprise customers that use the Customer Self-Service Portal will notice that comments logged in that system appear as Notes within Sugar.  So making sure there are not any unread Notes can be important daily task for these users. But how do you know if you have viewed each Note already?

 

In this post we are going to explore how we can add Read and Unread statuses to the Notes module.

 

This idea is taken from the Sugar Notifications Module which also comes out of the box with Sugar. Basically we will have a new field type styled with Bootstrap labels. The Notes status field will be highlighted with a green label if it has been read and a red label if it is unread.

 

This new field will be available for Record, List and Subpanel List views. The status will automatically change to Read when any Sugar user opens and views a Note. Notes created through Customer Self-Service Portal will be marked Unread while those created in Sugar by regular Sugar users will be marked as Read.  However, Sugar users will also be provided the option to toggle this status by clicking the viewed status label.

 

We will also provide mass update functionality to update the status of many selected notes all at once!  Whew!

 

Step 1: Adding a new field for Notes Module

Our first step is to create a new status field for Notes module.

Navigate to Admin > Studio > Notes > Fields

Create a new drop down field with name 'is_read_c'. Select the list name as 'notifications_status_dom'. I have set the display label as 'Status'. But you can change the label as per your wish. Also keep the Mass Update check box checked.

 

Step 2: Create new Read field type

 

Our next step is to add a new field type called Read for the Notes module. Our new field is_read_c will use this new field type.

 

Read Field Controller

custom/modules/Notes/clients/base/fields/read/read.js

 

[gist read.js

({
/**Define our custom events for the field
  * */
    events: {
'click [data-action=toggle]': 'toggleIsRead',
'mouseover [data-action=toggle]': 'toggleMouse',
'mouseout [data-action=toggle]': 'toggleMouse'
    },
    plugins: ['Tooltip'],
/**
     *
     * The read field is always a readonly field.
     * If `mark_as_read` option is enabled on metadata it means we should
     * automatically mark the notification as read.
     *
*/
initialize: function(options) {
options.def.readonly = true;
this._super('initialize', [options]);

if (options.def && options.def.mark_as_read) {
if(_.isEmpty(this.model.get('id'))){
this.model.set('is_read_c',true);
  }else{
this.markAs(true);
  }
        }
    },

/**
     * Event handler for mouse events.
     *
     * @param {Event} event Mouse over / mouse out.
*/
toggleMouse: function(event) {
var $target= this.$(event.currentTarget),
            isRead = this.model.get('is_read_c');
if (!isRead) {
return;
        }
var label = event.type === 'mouseover' ? 'LBL_UNREAD' : 'LBL_READ';
$target.html(app.lang.get(label, this.module));
    },
/**
     * Toggle notification `is_read_c` flag.
*/
toggleIsRead: function() {
this.markAs(!this.model.get('is_read_c'));
    },
/**
     * Mark notification as read/unread.
     *
     * @param {Boolean} read `True` marks notification as read, `false` as
     *   unread.
*/
markAs: function(read) {
if (read === this.model.get('is_read_c')) {
return;
        }
this.model.save({is_read_c: !!read}, {
            success: _.bind(function() {
if (!this.disposed) {
this.render();
                }
            }, this)
        });
    }
})

]

 

Read Field Templates

 

Below we define detail and list view templates for the new field type.custom/modules/Notes/clients/base/fields/read/detail.hbs

 

[gist read_detail.hbs

{{!--
/*
* this template will be used when the field renders in record view.
*/
--}}
{{!-- restricting the field in create view --}}
{{#if model.id}}
<span data-action="toggle" class="label{{#if this.model.attributes.is_read_c}} label-success{{else}} label-important{{/if}}">
{{#if this.model.attributes.is_read_c}}{{str 'LBL_READ' this.module}}{{else}}{{str 'LBL_UNREAD' this.module}}{{/if}}
</span>
{{/if}}

]custom/modules/Notes/clients/base/fields/read/list.hbs

 

[gist read_list.hbs

{{!--
/*
* this template will be used when the field renders in list and subpanel list views.
*/
--}}
<span data-action="toggle" class="label{{#if this.model.attributes.is_read_c}} label-success{{else}} label-important{{/if}}">
{{#if this.model.attributes.is_read_c}}{{str 'LBL_READ' this.module}}{{else}}{{str 'LBL_UNREAD' this.module}}{{/if}}
</span>

]

 

Step 3: Add Status field to Notes View metadata

 

Now we will add the status field to Notes module views through extensions. Below steps will explain you how to add the fields to record and list view. You can add the extension file to add the field to subpanels as well.custom/Extension/modules/Notes/Ext/clients/base/views/record/record.php

 

[gist notes_record.php

<?php
$is_read = array(
'name' => 'is_read_c',
'type' => 'read',
'dismiss_label' => true,
'mark_as_read' => true, //if true note will be marked as read when the user opens the note
);
/** I am adding my status field as a last field in the header panel of notes record view
  * */
foreach ($viewdefs['Notes']['base']['view']['record']['panels'] as $key => $panel){
if($panel['name'] == 'panel_header'){
array_push($panel['fields'],$is_read);
$viewdefs['Notes']['base']['view']['record']['panels'][$key]['fields'] = $panel['fields'];
break;
  }
}

]

 



 

 custom/Extension/modules/Ext/Notes/clients/base/views/list/list.php

 

[gist notes_list.php

<?php
$is_read = array(
'name' => 'is_read_c',
'type' => 'read',
'default' => true,
'enabled' => true,
);
/** I am adding my status field to the second column in the header panel of notes list view
  * you can modify this code to place your field in column which you like
  * */
foreach ($viewdefs['Notes']['base']['view']['list']['panels'] as $key => $panel){
if($panel['name'] == 'panel_header'){
$fields = $panel['fields'];
$splice = array_splice($fields, 1);//splicing field array from second column onward
$newFields = array();
array_push($newFields,array_shift($fields),$is_read);
foreach($splice as $field){
array_push($newFields,$field);
   }
$viewdefs['Notes']['base']['view']['list']['panels'][$key]['fields'] = $newFields;
break;
  }
}



]

 



 

 

Step 4: Add display labels for the new field type

 

Our final step is to define the display label for our new field using Language extension.custom/Extension/modules/Notes/Ext/Language/sugarfield_is_read.lang.en_us.php

 

[gist sugarfield_is_read.lang.en_us.php

<?php
$mod_strings['LBL_READ'] = 'Read';
$mod_strings['LBL_UNREAD'] = 'Unread';
?>

]

 

Step 5: Run Quick Repair and Rebuild

 

Finally, you will need to run Quick Repair and Rebuild in order to build your new extensions. You will also need to do a hard refresh of the browser page in order to load the updated JavaScript and CSS files.

Here is a guest post from Shijin Krishna from BHEA, an Elite SugarCRM Partner, and active member of the Sugar Developer community.Are you interested in posting on the Sugar Developer Blog? Contact developers@sugarcrm.com with your idea.

 

In this post we are going to see how we can add a quick create pop-up view for related modules in record view. This idea is taken from Sugar Portal which comes with Sugar Enterprise, Ultimate and Corporate editions. Sugar Portal’s detail view for Bugs and Cases modules includes an action to add a note to the bug or case. In this post, we are going to see how we can create a similar quick create pop up on the base Sugar 7 web client which will be shown at the click of a button.

 

We will use Cases and Notes modules as our basis.  We shall place a new ‘Add a Note’ button in Cases record view.  This button will be wired to display a pop-up create view for adding notes on the current case record.

The terms modal and popup are sometimes used interchangeably in Sugar 7 codebase and documentation. A modal dialog requires that a user interacts with it before they can continue. In many frameworks such as Bootstrap, modals are implemented as a child frame that "pops up" on the application screen.  In Sugar 7, Drawers are more commonly used than popups.

 

Adding a new Record View button

 

The following steps will create a new custom button on the Cases module’s Record View and wire it to a JavaScript callback function. These steps has been taken from Matt’s blog post on Leveraging Backbone Events in Sugar.

 

Step 1: Insert Button Metadata

 

We need to add Button metadata to the existing Cases record view using a Sidecar Extension. This defines the Sidecar Event to be triggered as well as the target object for the event.custom/Extension/modules/Cases/Ext/clients/base/views/record/add_note_button.php

 

[gist add_note_button.php

<?php
//Insert our custom button definition into existing Buttons array before the edit button
array_splice($viewdefs['Cases']['base']['view']['record']['buttons'], -2, 0, array(
array(
'name' => 'add_note',
'type' => 'button',
'label' => 'LBL_ADD_NOTE',
'css_class' => 'btn-success',//css class name for the button
'events' => array(
// custom Sidecar Event to trigger on click.  Event name can be anything you want.
'click' => 'button:add_note:click',
        )
    ),
));

]

 

Step 2: Defining the Sidecar Event to be triggered on click of the button

 

To define the event to be triggered on click of our custom button, we will override the record view controller for Cases.custom/modules/Cases/clients/base/views/record/record.js

 

[gist cases_record.js

 /**
  * @extends View.Views.Base.RecordView
*/
({
extendsFrom:'RecordView',
initialize:function(options){
this._super('initialize',[options]);
this.context.on('button:add_note:click',this._openNoteModal,this);
},
/**Function to open the note create pop-up*/
_openNoteModal: function() {
/**add class content-overflow-visible if client has touch feature*/
if (Modernizr.touch) {
app.$contentEl.addClass('content-overflow-visible');
  }
/**check whether the view already exists in the layout.
   * If not we will create a new view and will add to the components list of the record layout
   * */
var quickCreateView = this.layout.getComponent('quick-create');
if (!quickCreateView) {
/** Prepare the context object for the new quick create view*/
var context = this.context.getChildContext({
    module: 'Notes',
    forceNew: true,
    create: true,
    link:'notes', //relationship name
   });
context.prepare();
/** Create a new view object */
   quickCreateView = app.view.createView({
    context:context,
    name: 'quick-create',
    layout: this.layout,
    module: context.module,
   });
/** add the new view to the components list of the record layout*/
this.layout._components.push(quickCreateView);
this.layout.$el.append(quickCreateView.$el);
  }
/**triggers an event to show the pop up quick create view*/
this.layout.trigger("app:view:quick-create");
},
})

]

 

Adding a new Quick Create View for Notes Module

 

Our next step is to create a new custom quick create popup view for Notes module. We will extend our custom view from the Baseeditmodal view.

 

File Structure

 

We’re going to create a folder in custom/modules/Notes/clients/base/views/ called “quick-create“. Inside that folder we will create 3 files:

  • quick-create.php
  • quick-create.js
  • quick-create.hbs

 

Your file system should look something like the following screenshot.

 

Step 1: Implement the Quick Create View Metadata (.php file)

 

The following metadata can be modified according to your needs. If you wish to see more fields in the quick create popup then you can add those fields as well to the fields array.custom/modules/Notes/clients/base/views/quick-create/quick-create.php

 

[gist quick-create.php

<?php
/** Metdata for the add note custom popup view
* The buttons array contains the buttons to be shown in the popu
* The fields array can be modified accordingly to display more number of fields if required
* */
$viewdefs['Notes']['base']['view']['quick-create'] = array(
'buttons' => array(
array(
'name' => 'cancel_button',
'type' => 'button',
'label' => 'LBL_CANCEL_BUTTON_LABEL',
'value' => 'cancel',
'css_class' => 'btn-invisible btn-link',
        ),
array(
'name' => 'save_button',
'type' => 'button',
'label' => 'LBL_SAVE_BUTTON_LABEL',
'value' => 'save',
'css_class' => 'btn-primary',
        ),
    ),
'panels' => array(
array(
'fields' => array(
0 =>
array(
'name' => 'name',
'default' => true,
'enabled' => true,
'width' => 35,
'required' => true //subject is required
                ),
1 =>
array(
'name' => 'description',
'default' => true,
'enabled' => true,
'width' => 35,
'required' => true, //description is required
'rows' => 5,
                ),
2 =>
array(
'name' => 'filename',
'default' => true,
'enabled' => true,
'width' => 35,
                ),
            )
        )
    )
);

]

 

Step 2: Implement the Quick Create View Controller (.js file)

 

Here is the JavaScript controller for our quick create view. We will extend our view from BaseeditmodalView.custom/modules/Notes/clients/base/views/quick-create/quick-create.js

 

[gist quick-create.js

 /**
  * @class View.Views.Base.QuickCreateView
  * @alias SUGAR.App.view.views.BaseQuickCreateView
  * @extends View.Views.Base.BaseeditmodalView
*/
({
    extendsFrom:'BaseeditmodalView',
    fallbackFieldTemplate: 'edit',
initialize: function(options) {
app.view.View.prototype.initialize.call(this, options);
if (this.layout) {
this.layout.on('app:view:quick-create', function() {
this.render();
this.$('.modal').modal({
       backdrop: 'static'
      });
this.$('.modal').modal('show');
$('.datepicker').css('z-index','20000');
app.$contentEl.attr('aria-hidden', true);
$('.modal-backdrop').insertAfter($('.modal'));

/**If any validation error occurs, system will throw error and we need to enable the buttons back*/
this.context.get('model').on('error:validation', function() {
this.disableButtons(false);
      }, this);
     }, this);
    }
this.bindDataChange();
   },
/**Overriding the base saveButton method*/
saveButton: function() {
var createModel = this.context.get('model');

this.$('[name=save_button]').attr('data-loading-text', app.lang.get('LBL_LOADING'));
this.$('[name=save_button]').button('loading');

/** Disable the buttons during save.*/
this.disableButtons(true);
this.processModel(createModel);

/** saves the related note bean*/
createModel.save(null, {
     relate: true,
     fieldsToValidate: this.getFields(this.module),
     success: _.bind(function() {
this.saveComplete();
     }, this),
     error: _.bind(function() {
this.disableButtons(false);
     }, this)

    });
   },
/**Overriding the base cancelButton method*/
cancelButton: function() {
this._super('cancelButton');
app.$contentEl.removeAttr('aria-hidden');
this._disposeView();
   },
/**Overriding the base saveComplete method*/
saveComplete: function() {
this._super('saveComplete');
app.$contentEl.removeAttr('aria-hidden');
this._disposeView();
   },
/**Custom method to dispose the view*/
_disposeView:function(){
/**Find the index of the view in the components list of the layout*/
var index = _.indexOf(this.layout._components,_.findWhere(this.layout._components,{name:'quick-create'}));
if(index > -1){
/** dispose the view so that the evnets, context elements etc created by it will be released*/
this.layout._components[index].dispose();
/**remove the view from the components list**/
this.layout._components.splice(index, 1);
    }
   },
  })

]

 

Step 3: Implement the Quick Create View Template (.hbs file)

 

Below is the Handlebars template for our custom quick create view. We need to add some CSS style for the popup to make it look better. We have added a new CSS class ‘quick-create’ for the modal element so that we can selectively apply new styling to our quick create popup without affecting the style of other modal popups being used elsewhere in Sugar.

 

The template has a form which contains the fields and buttons defined in view metadata. We have used a book icon in the modal header from FontAwesome. If you are trying this example in any sugar version prior to 7.6.x then you may need to make sure you are using a different FontAwesome class name for this icon. Refer to this blog post for more details.

 

You can find the available list of icons in Sugar Styleguide accessible from the Administration panel of your Sugar instance.custom/modules/Notes/clients/base/views/quick-create/quick-create.hbs

 

[gist quick-create.hbs

<div class="modal hide quick-create">
    <div class="modal-header">
        <a class="close" data-dismiss="modal"><i class="fa fa-times"></i></a>
        <h3><i class="fa fa-book"></i>  {{str "LBL_CREATE_NOTE" module}}</h3>
    </div>
    <div class="modal-body">
        <form class="form-horizontal" enctype="multipart/form-data" method="POST">
            <fieldset>
{{#each meta.panels}}
{{#each fields}}
                <div class="row-fluid control-group">
                <label class="span3">{{str this.label ../../this.model.module}}</label>
                <div class="span9">{{field ../../this model=../../context.attributes.createModel template="edit"}}</div>
                </div>
{{/each}}
{{/each}}
            </fieldset>
        </form>
    </div>
    <div class="modal-footer">
{{#each meta.buttons}}
{{field ../this model=../createModel}}
{{/each}}
    </div>
</div>

]

 

Step 4: Add custom CSS style for our Popup (.less file)

 

Feel free to change the style as you wish!custom/themes/custom.less

 

[gist quick_create_popup_custom.less

.quick-create{
border:none;
}
.quick-create .modal-header h3 {
font-size:15px;
font-weight: normal;
}
.quick-create .modal-header{
background: #610319;
color: #FFF;
}
.quick-create .modal-body{
padding: 5px 0 58px 12px;
}
.quick-create .modal-body .row-fluid {
margin-top: 10px;
}
.quick-create label.span3{
font-size: 13px;
color: #797979;
}
.quick-create .fa-book{
color: #FFF;
}

]

 

Step 5: Define the display labels for our new UI

 

Our final step is to define the display label for the custom record view button and for the quick create pop up header.custom/Extension/modules/Cases/Ext/Language/add_note.en_us.lang.php

 

[gist quick_create_view.en_us.lang.php

<?php
$mod_strings['LBL_ADD_NOTE'] = 'Add a Note';
$mod_strings['LBL_CREATE_NOTE'] = 'Create Note';

]

 

Step 6: Quick Repair and Rebuild

 

Finally, you will need to run Quick Repair and Rebuild in order to build your new extensions. You will also need to do a hard refresh of the browser page in order to load the updated JavaScript and CSS files.

 

After this, you will be ready to use your new quick create popup dialog. Visit the Cases module and click the Add a Note button and you should see a dialog like this one below.