Graph Dashlet doesn't accept configure option

I’m trying add some new dashlets on SuiteCRM, they work fine, but my popup for filtering options doesn’t work.

I select the options I want to be shown and when I click on save nothing happens. It’s a graph dashlet.

Other dashlets work fine when I filter the options, but none of the ones I created work.

I think it must be something obvious but I’ve already tried everything i could.

I’m guessing it is because it isn’t registering the options I selected, but I have no idea how to fix this, and I don’t really know if this is really the issue.

My log shows “[2947][1][FATAL] Exception in Controller: String could not be parsed as XML”, but I tried to work on that with things I saw suggested here, but it didn’t have any effect.

MyDashlet.php


require_once('include/Dashlets/DashletGenericChart.php');

class MyDashlet extends DashletGenericChart
{
    public $pbss_date_start;
    public $pbss_date_end;
    public $sales_stage = array();

    /**
     * @see DashletGenericChart::$_seedName
     */
    protected $_seedName = 'Opportunities';

    /**
     * @see DashletGenericChart::__construct()
     */
    public function __construct(
        $id,
        array $options = null
    )
    {
        global $timedate;

        if(empty($options['pbss_date_start']))
            $options['pbss_date_start'] = $timedate->nowDbDate();

        if(empty($options['pbss_date_end']))
            $options['pbss_date_end'] = $timedate->asDbDate($timedate->getNow()->modify("+6 months"));

        //if(empty($options['title']))
            $options['title'] = '<a href="http://crmpesa.wikiconsultoria.com.br/index.php?module=AOR_Reports&action=DetailView&record=d6e1923d-a5de-53fe-7202-576bd580630b" target="_blank">Funil de Oportunidades</a>';			
		$options['autoRefresh'] = 60;

        parent::__construct($id,$options);
    }

    /**
     * @see DashletGenericChart::displayOptions()
     */
    public function displayOptions()
    {
        global $app_list_strings;
		
        if (!empty($this->sales_stage) && count($this->sales_stage) > 0)
            foreach ($this->sales_stage as $key)
                $selected_datax[] = $key;
        else
            $selected_datax = array_keys($app_list_strings['sales_stage']);

        $this->_searchFields['sales_stage']['options'] = $app_list_strings['sales_stage'];
        $this->_searchFields['sales_stage']['input_name0'] = $selected_datax;
        
		
		$GLOBALS['log']->fatal("----------------------------- Tipo: ".count($this->sales_stage));
		
        return parent::displayOptions();
    }

    /**
     * @see DashletGenericChart::display()
     */
    public function display()
    {
        global $current_user, $sugar_config, $app_list_strings;

        $is_currency = true;
        $thousands_symbol = translate('LBL_OPP_THOUSANDS', 'Charts');

        $currency_symbol = $sugar_config['default_currency_symbol'];
        if ($current_user->getPreference('currency')){

            $currency = new Currency();
            $currency->retrieve($current_user->getPreference('currency'));
            $currency_symbol = $currency->symbol;
        }else{
            $currency_symbol = 'R$ ';
        }


        $data = $this->getChartData($this->constructQuery());
        //$chartReadyData = $this->prepareChartData($data, $currency_symbol, $thousands_symbol);

        //$jsonData = json_encode($chartReadyData['data']);
       // $jsonLabels = json_encode($chartReadyData['labels']);
       // $jsonLabelsAndValues = json_encode($chartReadyData['labelsAndValues']);
        
       
        $i = 0;
        $dados = '';
//Montando o Json
        /*foreach($chartReadyData['data'] as $value){
        
           $chave = $chartReadyData['labels'][$i];
	   $label = $app_list_strings['sales_stage_dom'][$chave];
           $dados= $dados."['".$label."',".$value."],";
           $i++;
        }*/
        //echo $dados;

        //$total = $chartReadyData['total'];

        $startDate = $this->pbss_date_start;
        $endDate = $this->pbss_date_end;

        //TODO find a better way of doing this
        $canvasId = 'rGraphFunnel'.uniqid();

        //These are taken in the same fashion as the hard-coded array above
        $module = 'Opportunities';
        $action = 'index';
        $query  ='true';
        $searchFormTab ='advanced_search';

        $chartWidth     = 900;
        $chartHeight    = 500;

        $autoRefresh = $this->processAutoRefresh();//$autoRefresh

        $colours = "['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99','#b15928']";
        //<canvas id='$canvasId' width='$chartWidth' height='$chartHeight'>[No canvas support]</canvas>
        //<canvas id='test123'  width='$chartWidth' height='$chartHeight'>[No canvas support]</canvas>

        //There is always an ending anchor value, hence this check is that the data array is less than 2
        /*if(!is_array($chartReadyData['data'])||count($chartReadyData['data']) < 2)
        {
            return "<h3 class='noGraphDataPoints'>$this->noDataMessage</h3>";
        }*/
		
		$notChart = '	<div class="oddListRowS1">
							<div class="footable-visible footable-last-column footable-first-column" style="padding-top: 10px;
							padding-bottom: 10px; padding-left: 5px;"><em>Sem Dados</em></div>
						</div> ';

        $chart = "<div id='$canvasId'  class='resizableCanvas'  style='auto; height:auto; margin:auto;'></div>
        <input type='hidden' class='startDate' value='$startDate' />
        <input type='hidden' class='endDate' value='$endDate' />
        <input type='hidden' class='module' value='$module' />
        <input type='hidden' class='action' value='$action' />
        <input type='hidden' class='query' value='$query' />
        <input type='hidden' class='searchFormTab' value='$searchFormTab' />
        <script>
        
   $(function () {

    $('#$canvasId').highcharts({
								chart: {
									type: 'funnel',
									marginRight: 150
								},
								title: {
									text: '',
									x: 50
								},
								plotOptions: {
									series: {
										dataLabels: {
											enabled: true,
											format: '<b>{point.name}</b> ({point.y:,.0f})',
											color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black',
											softConnector: true
										}
									}
								},
								legend: {
									enabled: false
								},
								series: [{
									name: 'Quantidade',
									data: [$data]
								}]
    });
});


        </script>
";

		$result = ($data == null) ? $notChart : $chart;
		
        return $result;
    }
	
	
	function getChartData($query)
    {
		global $db;
		
        $result = $db->query($query);
	
		while($row = $db->fetchByAssoc($result, false)) {
		
			$estagio = str_replace("_", " ", $row['sales_stage']);

		
			$data = $data."['".utf8_encode($estagio)."', ".$row['qtde']."], ";
		}
		
		$data = ltrim($data, ", ");	
		
        return $data;
	}

    /**
     * awu: Bug 16794 - this function is a hack to get the correct sales stage order until
     * i can clean it up later
     *
     * @param  $query string
     * @return array
     */
    /*function getChartData(
        $query
    )
    {
        global $app_list_strings, $db;

        $data = array();
        $temp_data = array();
        $selected_datax = array();

        $user_sales_stage = $this->pbss_sales_stages;
        $tempx = $user_sales_stage;

        //set $datax using selected sales stage keys
        if (count($tempx) > 0) {
            foreach ($tempx as $key) {
                $datax[$key] = $app_list_strings['sales_stage_dom'][$key];
                $selected_datax[] = $key;
            }
        }
        else {
            $datax = $app_list_strings['sales_stage_dom'];
            $selected_datax = array_keys($app_list_strings['sales_stage_dom']);
        }
        


        $result = $db->query($query);
        while($row = $db->fetchByAssoc($result, false))
            $temp_data[] = $row;

        // reorder and set the array based on the order of selected_datax
        foreach($selected_datax as $sales_stage){
        	foreach($temp_data as $key => $value){
        		if ($value['sales_stage'] == $sales_stage){
        			$value['sales_stage'] = $app_list_strings['sales_stage_dom'][$value['sales_stage']];
        			$value['key'] = $sales_stage;
        			$value['value'] = $value['sales_stage'];
        			$data[] = $value;
        			unset($temp_data[$key]);
        		}
        	}
        }
        return $data;
    }*/

    /**
     * @see DashletGenericChart::constructQuery()
     */
		protected function constructQuery()
		{
			global $current_user;
			
			$id_user = $current_user->id;
			
			$query = "  SELECT O.sales_stage, count(*) AS qtde
							FROM opportunities O
						JOIN
							opportunities_cstm OC ON OC.id_c = O.id";					
			$query .= " WHERE O.assigned_user_id = '".$id_user."' 
							  AND O.date_entered >= ". db_convert("'".$this->pbss_date_start."'",'date').
							" AND O.date_entered <= ".db_convert("'".$this->pbss_date_end."'",'date') .
							" AND O.deleted = 0 ";
			if (count($this->sales_stage) > 0)
				$query .= " AND O.sales_stage IN ('" . implode("','",$this->sales_stage) . "') ";
			$query .= " GROUP BY O.sales_stage ORDER BY O.sales_stage DESC ";
			
			
			return $query;
		}
        /*protected function constructQuery()
        {
            $query = "  SELECT opportunities.sales_stage,
                            users.user_name,
                            opportunities.assigned_user_id,
                            count(*) AS opp_count,
                            sum(amount_usdollar/1000) AS total
                        FROM users,opportunities  ";
            $query .= " WHERE opportunities.date_closed >= ". db_convert("'".$this->pbss_date_start."'",'date').
                            " AND opportunities.date_closed <= ".db_convert("'".$this->pbss_date_end."'",'date') .
                            " AND opportunities.assigned_user_id = users.id  AND opportunities.deleted=0 ";
            if ( count($this->pbss_sales_stages) > 0 )
                $query .= " AND opportunities.sales_stage IN ('" . implode("','",$this->pbss_sales_stages) . "') ";
            $query .= " GROUP BY opportunities.sales_stage ,users.user_name,opportunities.assigned_user_id";

            return $query;
        }*/
   /*
    protected function constructQuery()
    {
        $query = "  SELECT opportunities.sales_stage,
                        count(*) AS opp_count,
                        sum(amount_usdollar/1000) AS total
                    FROM users,opportunities  ";
        $query .= " WHERE opportunities.date_closed >= ". db_convert("'".$this->pbss_date_start."'",'date').
            " AND opportunities.date_closed <= ".db_convert("'".$this->pbss_date_end."'",'date') .
            " AND opportunities.assigned_user_id = users.id  AND opportunities.deleted=0 ";
        $query .= " GROUP BY opportunities.sales_stage";

        return $query;
    }*/

    protected function prepareChartData($data,$currency_symbol, $thousands_symbol)
    {
        //return $data;
        $chart['labels']=array();
        $chart['data']=array();
        $total = 0;
        foreach($data as $i)
        {
            //$chart['labelsAndValues'][]=$i['key'].' ('.$currency.(int)$i['total'].')';
            $chart['labelsAndValues'][]=$i['key'].' ('.$currency_symbol.(int)$i['total'].$thousands_symbol.')';
            $chart['labels'][]=$i['key'];
            $chart['data'][]=(int)$i['total'];
            $total+=(int)$i['total'];
        }
        //The funnel needs n+1 elements (to bind the shape to as per http://www.rgraph.net/demos/funnel-interactive-key.html)
        //$chart['data'][]=1;
        $chart['total']=$total;
        return $chart;
    }


}

MyDashletConfigure.tpl


<div style='width: 400px'>
<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='Opportunities'>
<input type='hidden' name='action' value='DynamicAction'>
<input type='hidden' name='DynamicAction' value='configureDashlet'>
<input type='hidden' name='to_pdf' value='true'>
<input type='hidden' name='configure' value='true'>
<input type='hidden' id='dashletType' name='dashletType' value='{$dashletType}' />

<table width="400" cellpadding="0" cellspacing="0" border="0" class="edit view" align="center">
<tr>
    <td valign='top' class='dataLabel' nowrap>{$LBL_TITLE} <br /></td>
    <td valign='top' class='dataField'>
    	<input type="text" value="{$dashlet_title}" size="30" name="mypbss_dashlet_title"/>
    </td>
</tr>
<tr>
    <td valign='top' nowrap class='dataLabel'>{$LBL_DATE_START} <br><i>{$user_date_format}</i></td>
    <td valign='top' class='dataField'>
    	<input onblur="parseDate(this, '{$cal_dateformat}');" class="text" name="mypbss_date_start" size='12' maxlength='10' id='date_start' value='{$date_start}'>
    	{sugar_getimage name="jscalendar" ext=".gif" alt=$LBL_ENTER_DATE other_attributes='align="absmiddle" id="date_start_trigger" '}
    </td>
</tr>
<tr>
    <td valign='top' nowrap class='dataLabel'>{$LBL_DATE_END}<br><i>{$user_date_format}</i></td>
    <td valign='top' class='dataField'>
    	<input onblur="parseDate(this, '{$cal_dateformat}');" class="text" name="mypbss_date_end" size='12' maxlength='10' id='date_end' value='{$date_end}'>
    	{sugar_getimage name="jscalendar" ext=".gif" alt=$LBL_ENTER_DATE other_attributes='align="absmiddle" id="date_end_trigger" '}
    </td>
</tr>

    <tr>
    <td valign='top' class='dataLabel' nowrap>{$LBL_SALES_STAGES}</td>
    <td valign='top' class='dataField'>
    	<select name="sales_stage[]" multiple size='3'>
    		{$selected_datax}
    	</select></td>
    </tr>
	

<tr>
    <td align="right" colspan="2">
        <input type='submit' onclick="" class='button' value='{$LBL_SUBMIT_BUTTON_LABEL}'>
   	</td>
</tr>
</table>
</form>
{literal}
<script type="text/javascript">
Calendar.setup ({
    inputField : "date_start", ifFormat : "{/literal}{$cal_dateformat}{literal}", showsTime : false, button : "date_start_trigger", singleClick : true, step : 1, weekNumbers:false
});
Calendar.setup ({
    inputField : "date_end", ifFormat : "{/literal}{$cal_dateformat}{literal}", showsTime : false, button : "date_end_trigger", singleClick : true, step : 1, weekNumbers:false
});
{/literal}
</script>
</div>

MyDashlet.data.php


$dashletData['MyDashlet']['searchFields'] = array(
        'pbss_date_start' => array(
                'name'  => 'pbss_date_start',
                'vname' => 'Data de:',
                'type'  => 'datepicker',
            ),
        'pbss_date_end' => array(
                'name'  => 'pbss_date_end',
                'vname' => utf8_encode('Data à:'),
                'type'  => 'datepicker',
            ),
        'sales_stage' => array(
                'name'  => 'sales_stage',
                'vname' => 'LBL_SALES_STAGES',
                'type'  => 'enum',
            ),
        );
?>

MyDashlet.meta.php


global $app_strings, $current_language;

$dashletMeta['MyDashlet'] = array('title'       => 'LBL_TITLE',  
                                                      'description' => 'LBL_TITLE',
                                                      'icon'		  => 'themes/default/images/icon_Charts_Funnel_32.gif',
                                                      'module'        => 'Opportunities', 
                                                      'category'    => 'Charts');

Don’t you have a specific line number where that error occurs, so you can check the variables, etc at that point?

No, that’s all I get on log.

I don’t know if this helps in any way, but on ‘modules/Charts/Dashlets/PipelineBySalesStageDashlet/PipelineBySalesStageDashlet.php’ the function displayOptions() is defined as this:


public function displayOptions()
    {
        global $app_list_strings;

        if (!empty($this->pbss_sales_stages) && count($this->pbss_sales_stages) > 0)
            foreach ($this->pbss_sales_stages as $key)
                $selected_datax[] = $key;
        else
            $selected_datax = array_keys($app_list_strings['sales_stage_dom']);

        $this->_searchFields['pbss_sales_stages']['options'] = $app_list_strings['sales_stage_dom'];
        $this->_searchFields['pbss_sales_stages']['input_name0'] = $selected_datax;

        return parent::displayOptions();
    }

I have a few other examples that work with ‘pbss_sales_stages’ even though in studio it’s named something else.

But if I try to use ‘pbss_sales_stages’ to configure my Dashlet, it shows – Not Implemented – on the filter popup, where my options should be.

Hhmm I really don’t know how to help you. I’ve never seen log messages that don’t show the line number…?

Of course if you can use a debugger and step through the code you should be able to find the problem…

1 Like