Custom Dashlets

JotPadDashletOptions.tpl

The JotPadDashletOptions.tpl file handles the display of the dashlet options.
./modules/Home/Dashlets/JotPadDashlet/JotPadDashletOptions.tpl
<div style='width: 500px'>
<form name='configure_{$id}' action="index.php" method="post" onSubmit='return SUGAR.dashlets.postForm("configure_{$id}", SUGAR.mySugar.uncoverPage);'>
<input type='hidden' name='id' value='{$id}'>
<input type='hidden' name='module' value='Home'>
<input type='hidden' name='action' value='ConfigureDashlet'>
<input type='hidden' name='to_pdf' value='true'>
<input type='hidden' name='configure' value='true'>
<table width="400" cellpadding="0" cellspacing="0" border="0" class="edit view" align="center">
<tr>
    <td valign='top' nowrap class='dataLabel'>{$titleLbl}
</td> 
   <td valign='top' class='dataField'>
        <input class="text" name="title" size='20' value='{$title}'>
    </td>
</tr>
<tr>
    <td valign='top' nowrap class='dataLabel'>{$heightLbl}</td>
    <td valign='top' class='dataField'>
        <input class="text" name="height" size='3' value='{$height}'>
    </td>
</tr>
<tr>
    <td align="right" colspan="2">
        <input type='submit' class='button' value='{$saveLbl}'>
    </td>
</tr>
</table>
</form>
</div>
The important thing to note here is the onSubmit. All configure forms should have this statement to uncover the page to remove the configuration dialog.
Note: It is important to separate your JavaScript into a separate JavaScript file. This is because Sugar Dashlets are dynamically added to a page through AJAX. The HTML included into JavaScript is not evaluated when dynamically included.
It is important that all JavaScript functions are included in this script file. Inline JavaScript (<a href onclick=’’ etc) will still function. If the Sugar Dashlet has JavaScript and a user dynamically adds it to the page, the Sugar Dashlet will not be accessible until after the user reloads the page.
Therefore it is beneficial to use as many generic methods in Dashlet.js as possible (Dashlets.callMethod() specifically!).

JotPadDashletScripts.tpl

The JotPadDashletScripts.tpl handles the generic javascript for the dashlet.
./modules/Home/Dashlets/JotPadDashlet/JotPadDashletScripts.tpl
{literal}<script>if(typeof JotPad == 'undefined') { // since the dashlet can be included multiple times a page, don't redefine these functions
    JotPad = function() {
        return {
            /**
             * Called when the textarea is blurred
             */
            blur: function(ta, id) {
                ajaxStatus.showStatus('{/literal}{$saving}{literal}'); // show that AJAX call is happening
                // what data to post to the dashlet
                var va=YAHOO.lang.JSON.stringify(ta.value);
                postData = 'to_pdf=1&module=Home&action=CallMethodDashlet&method=saveText&id=' + id + '&savedText=' + va;
                var cObj = YAHOO.util.Connect.asyncRequest('POST','index.php',{success: JotPad.saved, failure: JotPad.saved}, postData);
            },
            /**
             * Called when the textarea is double clicked on
             */
            edit: function(divObj, id) {
                ta = document.getElementById('jotpad_textarea_' + id);
                if(SUGAR.isIE) ta.value = divObj.innerHTML.replace(/<br>/gi, "\n");
                else ta.value = divObj.innerHTML.replace(/<br>/gi, '');
                ta.value = ta.value.replace(/&amp;/, "&");
                divObj.style.display = 'none';
                ta.style.display = '';
                ta.focus();
            },
            /**
             * handle the response of the saveText method
             */
            saved: function(data) {
                eval(data.responseText);
                ajaxStatus.showStatus('{/literal}{$saved}{literal}');
                if(typeof result != 'undefined') {
                    ta = document.getElementById('jotpad_textarea_' + result['id']);
                    theDiv = document.getElementById('jotpad_' + result['id']);
                    theDiv.innerHTML = result['savedText'];
                }
                ta.style.display = 'none';
                theDiv.style.display = '';
                window.setTimeout('ajaxStatus.hideStatus()', 2000);
            }
        };
    }();
}
</script>{/literal}

Refreshing the Sugar Dashlet Cache

To add a Sugar Dashlet to your SugarCRM installation, you can use the dashlets installdef in Module Loader to install your Sugar Dashlet Package to the ./custom/Home/Dashlets/ directory. If you are developing or need to copy the dashlet to a different modules directory, you will need to navigate to Admin > Repair > Rebuild Sugar Dashlets. This will rebuild the dashlet cache locatd in./cache/dashlets/dashlets.php that maps the dashlets.

Packaging Generic Sugar Dashlets

If you are packaging a generic dashlet that is not module specific, you can use the dashlets installdef. This will install the dashlet to ./custom/modules/Home/Dashlets/<dashlet>/.

manifest.php
<?php
   $manifest =array(
       'acceptable_sugar_flavors' => array(),
       'acceptable_sugar_versions' => array(),
       'author' => 'SugarCRM',
       'description' => 'Installs the dashlet using the dashlets installdef',       'icon' => '',
       'is_uninstallable' => true,
       'name' => 'Dashlet installer example',
       'published_date' => '2013-01-29 2013 13:49:58',
       'type' => 'module',
       'version' => '1.0',
   );
   $installdefs =array(
       'id' => 'package_1359467398',
       'dashlets' => array(
            0 => array(
                //The name to install the dashlet under
                'name' => 'MyDashlet',
                //This directory contains the dashlet files
                'from' => '<basepath>/MyDashlet',
            ),
        ),
    );
?>
If you are creating a module specific dashlet, you will have to move the dashlet to the directory using the copy installdef as shown below:
manifest.php
<?php
   $manifest =array(
       'acceptable_sugar_flavors' => array(),
       'acceptable_sugar_versions' => array(),
       'author' => 'SugarCRM',
       'description' => 'Installs the dashlet using the copy installdef',
       'icon' => '',
       'is_uninstallable' => true,
       'name' => 'Dashlet installer example',
       'published_date' => '2013-01-29 2013 13:49:58',
       'type' => 'module',
       'version' => '1.0',
   );
   $installdefs =array(
       'id' => 'package_1359467399',
       'copy' => array(
            0 => array(
                'from' => '<basepath>/MyDashlet/',
                'to' => 'custom/modules/<module>/Dashlets/MyDashlet',
            ),
        ),
    );
?>
Please note that if you are installing a dashlet using the copy installdef, you will need to navigate to Admin > Repair > Rebuild Sugar Dashlets. This will rebuild the dashlet cache.

More information on the manifest file can be found in the Introduction to the Manifest section.

Creating Custom Chart Dashlets

Creating a custom chart dashlet is very similar to creating the MyAccountsDashlet described above. The main difference is that you will need to override the display() method in your class to build the chart, using the SugarChartFactory class included with SugarCRM. Beginning in Sugar 6.2, we have switched the charts to be rendered through JavaScript. The SugarChartFactory returns a subclass of SugarChart. See below for an example of display() method as used in the Outcome by Month dashlet.

./modules/Charts/Dashlets/OutcomeByMonthDashlet/OutcomeByMonthDashlet.php


    /**
     * @see DashletGenericChart::display()
     */    public function display()
    {
        $currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
        if ($GLOBALS['current_user']->getPreference('currency')){
            $currency = new Currency();
            $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
            $currency_symbol = $currency->symbol;
        }
        require("modules/Charts/chartdefs.php");
        $chartDef = $chartDefs['outcome_by_month'];
        require_once('include/SugarCharts/SugarChartFactory.php');
        $sugarChart = SugarChartFactory::getInstance();
        $sugarChart->setProperties('',
            translate('LBL_OPP_SIZE', 'Charts') . ' ' . $currency_symbol . '1' .translate('LBL_OPP_THOUSANDS', 'Charts'), $chartDef['chartType']);
        $sugarChart->base_url = $chartDef['base_url'];
        $sugarChart->group_by = $chartDef['groupBy'];
        $sugarChart->url_params = array();
        $sugarChart->getData($this->constructQuery());
        $sugarChart->is_currency = true;
        $sugarChart->data_set = $sugarChart->sortData($sugarChart->data_set, 'm', false, 'sales_stage', true, true);
        $xmlFile = $sugarChart->getXMLFileName($this->id);
        $sugarChart->saveXMLFile($xmlFile, $sugarChart->generateXML());
           return $this->getTitle('<div align="center"></div>') . '<div align="center">' . $sugarChart->display($this->id, $xmlFile, '100%', '480', false) . '</div>'. $this->processAutoRefresh();
    }
    /**
     * @see DashletGenericChart::constructQuery()
     */
    protected function constructQuery()
    {
        $query = "SELECT sales_stage,". db_convert('opportunities.date_closed','date_format',array("'%Y-%m'"),array("'YYYY-MM'"))." as m, ". "sum(amount_usdollar/1000) as total, count(*) as opp_count FROM opportunities ";
        $this->getSeedBean()->add_team_security_where_clause($query);
        $query .= " WHERE opportunities.date_closed >= ".db_convert("'".$this->obm_date_start."'",'date') . " AND opportunities.date_closed <= ".db_convert("'".$this->obm_date_end."'",'date') . " AND opportunities.deleted=0";
        if (count($this->obm_ids) > 0)
            $query .= " AND opportunities.assigned_user_id IN ('" . implode("','",$this->obm_ids) . "')";
            $query .= " GROUP BY sales_stage,". db_convert('opportunities.date_closed','date_format',array("'%Y-%m'"),array("'YYYY-MM'")) . " ORDER BY m";
        return $query;
    }