Skip navigation
All Places > Developer > Blog > 2016 > March
2016
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.

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.

hats

Sugar Notifications in Action

Posted by hats Mar 14, 2016
Here is a guest post from Hatim Alam from BHEA, an Elite SugarCRM Partner, and is a certified Sugar Developer Specialist. Hatim shares some ideas for getting creative use out of Sugar Notifications.  Share your own ideas in comments below!

 

In this post, we are going to look into different scenarios where Sugar Notifications can be of great help. For example, It can be used to notify user when new post or comment has been created on the Activity Stream or on execution of custom job or on triggering of API endpoint or implementing approval/review workflow.

 

We will create Sugar Notification for each of these scenarios and learn how easy it is to use and implement.

 



 

What are Sugar Notifications?

 

The Notifications indicator is available to all Sugar users and is located on the upper right of the screen next to global search.

 

The number displayed in the notifications box indicates the number of messages with a status of "Unread". A "0" will appear if there are no unread notifications.

 

Open the Notifications dropdown and click "View Notifications" in order to see all notifications in a list view.

 



 

 

Create Sugar Notifications on new posts or comments in the Activity Stream

Editor's Note:  Pedro Bazan had published popular a logic hook for adding notifications whenever a user is mentioned in an Activity Stream.

 

The following steps will create new before_save logic hook on Activities bean within Activity Stream module. Through this logic hook, we will create a new notification whenever a user posts a new comment.

 

Step 1: Create before_save Logic Hook extension definition

custom/Extension/modules/ActivityStream/Activities/Ext/LogicHooks/create_notification_save.php

 

create_notification_save.php

<?php
$hook_version = 1;
$hook_array = array();
$hook_array['before_save'] = array();
$hook_array['before_save'][] = array(
1,
'Create Sugar Notification on any new post or comment',
'custom/modules/ActivityStream/Activities/customLogicHook.php',
'SugarNotifyUser',
'notify_user_on_post_comment'
);
?>

 

Step 2: Create the Logic Hook action method

 

As defined in our logic hook extension above, now we need to create a file where we can define our logic hook function.  In this action, we will check if any new post or comment has been posted to Activity Stream.

 

Usually, there are multiple types of activities in Activity Stream - link, unlink, create, update and post. We will be checking if the activity type is a post.  The post activity type is associated with new posts or comments within an Activity Stream.custom/modules/ActivityStream/Activities/customLogicHook.php

 

customLogicHooks.php

<?php

/**
* Desc: Before save logic hook to create Sugar Notification if new post or comment in Activity Stream
* Written by: Hatim Alam
* Dated: 21st Feb 2016
*/

class SugarNotifyUser {
function notify_user_on_post_comment($bean, $event, $arguments) {
//if activity type is a post
if($bean->activity_type=="post") {
//get the parent bean
$parent_bean = BeanFactory::getBean($bean->parent_type, $bean->parent_id);
//initialize notification bean
$notification_bean = BeanFactory::getBean("Notifications");
//check if comment or a post
$notification_bean->name = ($bean->activity_type=="post" && $bean->comment_count==0) ? "New post on {$bean->parent_type}" : "New comment on {$bean->parent_type}";
$notification_bean->description = "New update has been posted on <a href='#{$bean->parent_type}/{$parent_bean->id}'>{$parent_bean->name}</a>";
//assigned user should be record assigned user
$notification_bean->assigned_user_id = $parent_bean->assigned_user_id;
$notification_bean->parent_id = $bean->parent_id;
$notification_bean->parent_type = $bean->parent_type;
$notification_bean->created_by = $bean->created_by;
//set is_read to no
$notification_bean->is_read = 0;
//set the level of severity
$notification_bean->severity = "information";
$notification_bean->save();
  }
}
}

?>

 

 

Create Sugar Notifications to update status of data synchronization

 

We have many situations where we integrate Sugar with external applications and expose our custom APIs to update data in Sugar as external changes happen. We can use notifications to increase user awareness of these changes. For eg; we can update contact information and notify the assigned user using Sugar Notifications.

 

Step 1: Create custom endpoint in Sugar

custom/modules/Contacts/clients/base/api/TestContactApi.php

 

TestContactApi.php

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

class TestContactAPi extends SugarApi
{
public function registerApiRest()
{
return array(
'testContactUpdate' => array(
//request type
'reqType' => 'POST',
//endpoint path
'path' => array('Contacts', 'testContactUpdate'),
//endpoint variables
'pathVars' => array(),
//method to call
'method' => 'testContactUpdate',
//short help string to be displayed in the help documentation
'shortHelp' => 'This is test endpoint to update contact',
//long help to be displayed in the help documentation
'longHelp' => '',
            ),
  );
}

public function testContactUpdate($api, $args)
{
//logic to update your contact info based on arguments received goes here

//if contact updated successfully, notify user
$notification_bean = BeanFactory::getBean("Notifications");
$notification_bean->name = "Contact updated successfully.";
$notification_bean->message = "Details of the update can go here.";
$notification_bean->parent_type = "Contacts";
$notification_bean->parent_id = "{$contact_id}"; //where $contact_id will come as an argument to api
$notification_bean->assigned_user_id = "{$contact_assigned_user_id}"; //where $contact_assigned_user_id is assigned user id of contact
$notification_bean->is_read = 0;
//set the level of severity
$notification_bean->severity = "information";
$notification_bean->save();
}
}

?>

 

 

Create Sugar Notifications to implement approval or review workflows

 

Sugar Notifications can be very handy to implement approval/review workflow and notify a user or a user's manager if any specific condition satisfies. For eg: the assigned user's Manager gets a notification when an opportunity is created with a discount > 25%.

 

Step 1: Create before_save logic hook extension definition

custom/Extension/modules/Opportunities/Ext/LogicHooks/check_opp_discount.php

 

check_opp_discount.php

<?php
$hook_version = 1;
$hook_array = array();
$hook_array['before_save'] = array();
$hook_array['before_save'][] = array(
1,
'Check discount percentage on opp save',
'custom/modules/Opportunities/customLogicHooks.php',
'CheckOppDiscount',
'check_opp_discount'
);
?>

 

Step 2: Create the logic hook action method

 

Here, we will check if opportunity discount percentage is >25%, we will notify assigned user's Manager using Sugar Notificationscustom/modules/Opportunities/customLogicHooks.php

 

customOppLogicHooks.php

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

class CheckOppDiscount {
function check_opp_discount($bean, $event, $arguments) {
//check if discount percentage is >25%
if($bean->opp_discount_c > 25) {
//get assigned user bean
$assigned_user_bean = BeanFactory::getBean("Users", $bean->assigned_user_id);
//fetch assigned user's manager
$manager_id = $assigned_user_bean->reports_to_id;
//create sugar notification
$notification_bean = BeanFactory::getBean("Notifications");
$notification_bean->name = 'Opportunity - {$bean->name} is in danger';
$notification_bean->description = 'Discount percentage is more than 25%.';
$notification_bean->parent_id = $bean->id;
$notification_bean->parent_type = 'Opportunities';
$notification_bean->assigned_user_id = $manager_id;
$notification_bean->severity = "warning";
$notification_bean->is_read = 0;
$notification_bean->save();
  }
}
}

?>

 

Se how easy it was to create Sugar Notifications? Sugar Notifications are a great tool for various other actions as well.  You can use them with custom APIs, logic hooks, Sugar Jobs, etc, in order to notify users of record assignment, new record creation, record update, job status, etc.Try out some ideas on your own and let us know what you come up with in the comments below!

 



Quick Tip: Check out this SugarCRM Knowledge Base article for adjusting Sugar Notification Delay - Setting Sugar's Notification Delay