Let the platform do the work

Dashlets

Overview

Dashlets are special view-component plugins that render data from a context and make use of the Dashlet plugin. They are typically made up of a controller JavaScript file (.js) and at least one Handlebars template (.hbs).

Hierarchy Diagram

Sugar loads the dashlet view components in the following manner:

Note: The Sugar application's client type is "base". For more information on the various client types, please refer to the User Interface page.

Dashlet Views

The are three views when working with dashlets: Preview, Dashlet View, and Configuration View. The following sections discuss the differences between views.

Preview

The preview view is used when selecting dashlets to add to your homepage. Preview variables in the metadata will be assigned to the custom model variables.

  'preview' => array(
    'key1' => 'value1',
),

The values in the preview metadata can be retrieved using:

this.model.get("key1");

Dashlet View

The dashlet view will render the content for the dashlet. It will also contain the settings for editing, removing, and refreshing the dashlet.

Configuration View

The configuration view is displayed when a user clicks the 'edit' option on the dashlet frame's drop-down menu. Config variables in the metadata will be assigned to the custom model variables

  'config' => array(
    //key value pairs of attributes
    'key1' => 'value1',
),

The values in the config metadata can be retrieved using:

this.model.get("key1");

Dashlet Example

The RSS feed dashlet, located in ./clients/base/views/rssfeed/, handles the display of RSS feeds to the user. The sections below outline the various files that render this dashlet.

Metadata

The Dashlet view contains the 'dashlets' metadata:

Parameters Type Required Description
label String yes The name of the dashlet
description String no A description of the dashlet
config Object yes Pre-populated variables in the configuration view
Note: Config variables in the metadata are assigned to the custom model variables.
preview Object yes  Pre-populated variables in the preview
filter Object no Filter for display

The RSS feed dashlets metadata is located in:

./clients/base/views/rssfeed/rssfeed.php

  <?php

/*
 * Your installation or use of this SugarCRM file is subject to the applicable
 * terms available at
 * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/.
 * If you do not agree to all of the applicable terms or do not have the
 * authority to bind the entity as an authorized representative, then do not
 * install or use this SugarCRM file.
 *
 * Copyright (C) SugarCRM Inc. All rights reserved.
 */

$viewdefs['base']['view']['rssfeed'] = array(
    'dashlets' => array(
        array(
            'label' => 'LBL_RSS_FEED_DASHLET',
            'description' => 'LBL_RSS_FEED_DASHLET_DESCRIPTION',
            'config' => array(
                'limit' => 5,
                'auto_refresh' => 0,
            ),
            'preview' => array(
                'limit' => 5,
                'auto_refresh' => 0,
                'feed_url' => 'http://blog.sugarcrm.com/feed/',
            ),
        ),
    ),
    'panels' => array(
        array(
            'name' => 'panel_body',
            'columns' => 2,
            'labelsOnTop' => true,
            'placeholders' => true,
            'fields' => array(
                array(
                    'name' => 'feed_url',
                    'label' => 'LBL_RSS_FEED_URL',
                    'type' => 'text',
                    'span' => 12,
                    'required' => true,
                ),
                array(
                    'name' => 'limit',
                    'label' => 'LBL_RSS_FEED_ENTRIES_COUNT',
                    'type' => 'enum',
                    'options' => 'tasks_limit_options',
                ),
                array(
                    'name' => 'auto_refresh',
                    'label' => 'LBL_DASHLET_REFRESH_LABEL',
                    'type' => 'enum',
                    'options' => 'sugar7_dashlet_reports_auto_refresh_options',
                ),
            ),
        ),
    ),
);

Controller

The rssfeed.js controller file, shown below, contains the JavaScript to render the news articles on the dashlet. The Dashlet view must include 'Dashlet' plugin and can override initDashlet to add additional custom process while it is initializing.

./clients/base/views/rssfeed/rssfeed.js

  /*
 * Your installation or use of this SugarCRM file is subject to the applicable
 * terms available at
 * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/.
 * If you do not agree to all of the applicable terms or do not have the
 * authority to bind the entity as an authorized representative, then do not
 * install or use this SugarCRM file.
 *
 * Copyright (C) SugarCRM Inc. All rights reserved.
 */
/**
 * RSS Feed dashlet consumes an RSS Feed URL and displays it's content as a list
 * of entries.
 * 
 * The following items are configurable.
 *
 * - {number} limit Limit imposed to the number of records pulled.
 * - {number} refresh How often (minutes) should refresh the data collection.
 *
 * @class View.Views.Base.RssfeedView
 * @alias SUGAR.App.view.views.BaseRssfeedView
 * @extends View.View
 */
({
    plugins: ['Dashlet'],

    /**
     * Default options used when none are supplied through metadata.
     *
     * Supported options:
     * - timer: How often (minutes) should refresh the data collection.
     * - limit: Limit imposed to the number of records pulled.
     *
     * @property {Object}
     * @protected
     */
    _defaultOptions: {
        limit: 5,
        auto_refresh: 0
    },

    /**
     * @inheritdoc
     */
    initialize: function(options) {
        options.meta = options.meta || {};
        this._super('initialize', [options]);
        this.loadData(options.meta);
    },

    /**
     * Init dashlet settings
     */
    initDashlet: function() {
        // We only need to handle this if we are NOT in the configure screen
        if (!this.meta.config) {
            var options = {};
            var self = this;
            var refreshRate;

            // Get and set values for limits and refresh
            options.limit = this.settings.get('limit') || this._defaultOptions.limit;
            this.settings.set('limit', options.limit);

            options.auto_refresh = this.settings.get('auto_refresh') || this._defaultOptions.auto_refresh;
            this.settings.set('auto_refresh', options.auto_refresh);

            // There is no default for this so there's no pointing in setting from it
            options.feed_url = this.settings.get('feed_url');

            // Set the refresh rate for setInterval so it can be checked ahead
            // of time. 60000 is 1000 miliseconds times 60 seconds in a minute.
            refreshRate = options.auto_refresh * 60000;

            // Only set up the interval handler if there is a refreshRate higher
            // than 0
            if (refreshRate > 0) {
                if (this.timerId) {
                    clearInterval(this.timerId);
                }
                this.timerId = setInterval(_.bind(function() {
                    if (self.context) {
                        self.context.resetLoadFlag();
                        self.loadData(options);
                    }
                }, this), refreshRate);
            }
        }

        // Validation handling for individual fields on the config
        this.layout.before('dashletconfig:save', function() {
            // Fields on the metadata
            var fields = _.flatten(_.pluck(this.meta.panels, 'fields'));

            // Grab all non-valid fields from the model
            var notValid = _.filter(fields, function(field) {
                return field.required && !this.dashModel.get(field.name);
            }, this);

            // If there no invalid fields we are good to go
            if (notValid.length === 0) {
                return true;
            }

            // Otherwise handle notification of invalidation
            _.each(notValid, function(field) {
                 var fieldOnView = _.find(this.fields, function(comp, cid) { 
                    return comp.name === field.name;
                 });

                 fieldOnView.model.trigger('error:validation:' + field.name, {required: true});
            }, this);

            // False return tells the drawer that it shouldn't close
            return false;
        }, this);
    },

    /**
     * Handles the response of the feed consumption request and sets data from 
     * the result
     * 
     * @param {Object} data Response from the rssfeed API call
     */
    handleFeed: function (data) {
        if (this.disposed) {
            return;
        }

        // Load up the template
        _.extend(this, data);
        this.render();
    },

    /**
     * Loads an RSS feed from the RSS Feed endpoint.
     * 
     * @param {Object} options The metadata that drives this request
     */
    loadData: function(options) {
        if (options && options.feed_url) {
            var callbacks = {success: _.bind(this.handleFeed, this), error: _.bind(this.handleFeed, this)},
                limit = options.limit || this._defaultOptions.limit,
                params = {feed_url: options.feed_url, limit: limit},
                apiUrl = app.api.buildURL('rssfeed', 'read', '', params);

            app.api.call('read', apiUrl, {}, callbacks);
        }
    },

    /**
     * @inheritdoc
     *
     * New model related properties are injected into each model:
     *
     * - {Boolean} overdue True if record is prior to now.
     */
    _renderHtml: function() {
        if (this.meta.config) {
            this._super('_renderHtml');
            return;
        }

        this._super('_renderHtml');
    }
})

Workflow

When triggered, the following procedure will render the view area:

Retrieving Data

Use the following commands to retrieve the corresponding data:

Data Location Element Command
Main pane Record View this.model
Record View this.context.parent.get("model")
List View this.context.parent.get("collection")
Metadata   this.dashletConfig['metadata_key']
Module vardefs   app.metadata.getModule("ModuleName")
Remote data Bean new app.data.createBean("Module")
new app.data.createBeanCollection("Module")
RestAPI  app.api.call(method, url, data, callbacks, options)
Ajax Call  $.ajax()
User inputs   this.settings.get("custom_key")

Handlebar Template

The rssfeed.hbs template file defines the content of the view. This view is used for rendering the markup rendering in the dashlet content.

./clients/base/views/rssfeed/rssfeed.hbs

  
{{!--
/*
 * Your installation or use of this SugarCRM file is subject to the applicable
 * terms available at
 * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/.
 * If you do not agree to all of the applicable terms or do not have the
 * authority to bind the entity as an authorized representative, then do not
 * install or use this SugarCRM file.
 *
 * Copyright (C) SugarCRM Inc. All rights reserved.
 */
--}}
{{#if feed}}
    <div class="rss-feed">
        <h4>
            {{#if feed.link}}<a href="{{feed.link}}">{{/if}}
            {{feed.title}}
            {{#if feed.link}}</a>{{/if}}
        </h4>
        <ul>
        {{#each feed.entries}}
            <li class="news-article">
                <a href="{{link}}">{{title}}</a>
                {{#if author}} - {{str "LBL_RSS_FEED_AUTHOR"}} {{author}}{{/if}}
            </li>
        {{/each}}
        </ul>
    </div>
{{else}}
    <div class="block-footer">
        {{#if errorThrown}}
        {{str "LBL_NO_DATA_AVAILABLE"}}
        {{else}}
        {{loading 'LBL_ALERT_TITLE_LOADING'}}
        {{/if}}
    </div>
{{/if}}