MOON
Server: Apache
System: Linux nserver.cafsindia.com 4.18.0-553.104.1.lve.el8.x86_64 #1 SMP Tue Feb 10 20:07:30 UTC 2026 x86_64
User: cafsindia (1002)
PHP: 8.2.30
Disabled: NONE
Upload Files
File: /home/cafsindia/lead_cafsinfotech.com/public/legacy/modules/AOR_Charts/AOR_Chart.php
<?php
/**
 * Advanced OpenReports, SugarCRM Reporting.
 * @package Advanced OpenReports for SugarCRM
 * @copyright SalesAgility Ltd http://www.salesagility.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
 * along with this program; if not, see http://www.gnu.org/licenses
 * or write to the Free Software Foundation,Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301  USA
 *
 * @author SalesAgility <info@salesagility.com>
 */

#[\AllowDynamicProperties]
class AOR_Chart extends Basic
{
    public const COLOUR_DEFAULTS = "['#1f78b4','#a6cee3','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99','#b15928','#144c73','#6caed1','#8acf4e','#20641c','#f8514f','#9e1214','#fc9d24','#b35900','#a880bb','#442763','#ffff4d','#733a1a']";
    public $colours;
    public $new_schema = true;
    public $module_dir = 'AOR_Charts';
    public $object_name = 'AOR_Chart';
    public $table_name = 'aor_charts';
    public $importable = true;
    public $disable_row_level_security = true ;

    public $id;
    public $name;
    public $date_entered;
    public $date_modified;
    public $modified_user_id;
    public $modified_by_name;
    public $created_by;
    public $created_by_name;
    public $description;
    public $deleted;
    public $created_by_link;
    public $modified_user_link;

    public $type;
    public $x_field;
    public $y_field;
    public $noDataMessage = "No Results";



    public function __construct()
    {
        parent::__construct();
        $this->colours = self::COLOUR_DEFAULTS;
    }




    public function save_lines(array $post, AOR_Report $bean, $postKey)
    {
        $seenIds = array();
        if (isset($post[$postKey.'id'])) {
            foreach ($post[$postKey . 'id'] as $key => $id) {
                if ($id && $post['record']!='') {
                    $aorChart = BeanFactory::getBean('AOR_Charts', $id);
                } else {
                    $aorChart = BeanFactory::newBean('AOR_Charts');
                }
                $aorChart->name = $post[$postKey . 'title'][$key];
                $aorChart->type = $post[$postKey . 'type'][$key];
                $aorChart->x_field = $post[$postKey . 'x_field'][$key];
                $aorChart->y_field = $post[$postKey . 'y_field'][$key];
                $aorChart->aor_report_id = $bean->id;
                $aorChart->save();
                $seenIds[] = $aorChart->id;
            }
        }
        //Any beans that exist but aren't in $seenIds must have been removed.
        foreach ($bean->get_linked_beans('aor_charts', 'AOR_Charts') as $chart) {
            if (!in_array($chart->id, $seenIds)) {
                $chart->mark_deleted($chart->id);
            }
        }
    }

    private function getValidChartTypes()
    {
        return array('bar','line','pie','radar','rose', 'grouped_bar', 'stacked_bar');
    }


    private function getColour($seed, $rgbArray = false)
    {
        $hash = md5($seed);
        $r = hexdec(substr($hash, 0, 2));
        $g = hexdec(substr($hash, 2, 2));
        $b = hexdec(substr($hash, 4, 2));
        if ($rgbArray) {
            return array('R'=>$r,'G'=>$g,'B'=>$b);
        }
        $highR = $r + 10;
        $highG = $g + 10;
        $highB = $b + 10;
        $main = '#'.str_pad(dechex($r), 2, '0', STR_PAD_LEFT)
            .str_pad(dechex($g), 2, '0', STR_PAD_LEFT)
            .str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
        $highlight = '#'.dechex($highR).dechex($highG).dechex($highB);
        return array('main'=>$main,'highlight'=>$highlight);
    }

    public function buildChartImageBar($chartPicture, $recordImageMap = false)
    {
        $scaleSettings = array("DrawSubTicks" => false, "LabelRotation" => 30, 'MinDivHeight' => 50);
        $chartPicture->drawScale($scaleSettings);
        $chartPicture->drawBarChart(array("RecordImageMap"=>$recordImageMap));
    }

    public function buildChartImagePie($chartPicture, $chartData, $reportData, $imageHeight, $imageWidth, $xName, $recordImageMap)
    {
        $PieChart = new pPie($chartPicture, $chartData);
        $x = 0;
        foreach ($reportData as $row) {
            $PieChart->setSliceColor($x, $this->getColour($row[$xName], true));
            $x++;
        }
        $PieChart->draw2DPie($imageWidth/3, $imageHeight/2, array("Border"=>true,'Radius'=>200,''=>true,"RecordImageMap"=>$recordImageMap));
        $PieChart->drawPieLegend($imageWidth*0.7, $imageHeight/3, array('FontSize'=>10,"FontName"=>"modules/AOR_Charts/lib/pChart/fonts/verdana.ttf",'BoxSize'=>14));
    }

    public function buildChartImageLine($chartPicture, $recordImageMap = false)
    {
        $scaleSettings = array("XMargin"=>10,"YMargin"=>10,"GridR"=>200,"GridG"=>200,"GridB"=>200,'MinDivHeight' => 50,"LabelRotation" => 30);
        $chartPicture->drawScale($scaleSettings);
        $chartPicture->drawLineChart(array("RecordImageMap"=>$recordImageMap));
    }

    public function buildChartImageRadar($chartPicture, $chartData, $recordImageMap)
    {
        $SplitChart = new pRadar();
        $Options = array("LabelPos"=>RADAR_LABELS_HORIZONTAL,"RecordImageMap"=>$recordImageMap);
        $SplitChart->drawRadar($chartPicture, $chartData, $Options);
    }

    public function buildChartImage(array $reportData, array $fields, $asDataURI = true, $generateImageMapId = false)
    {
        global $current_user;
        require_once 'modules/AOR_Charts/lib/pChart/pChart.php';

        if ($generateImageMapId !== false) {
            $generateImageMapId = $current_user->id."-".$generateImageMapId;
        }

        $html = '';
        if (!in_array($this->type, $this->getValidChartTypes())) {
            return $html;
        }
        $x = $fields[$this->x_field];
        $y = $fields[$this->y_field];
        if (!$x || !$y) {
            //Malformed chart object - missing an axis field
            return '';
        }
        $xName = str_replace(' ', '_', (string) $x->label) . $this->x_field;
        $yName = str_replace(' ', '_', (string) $y->label) . $this->y_field;

        $chartData = new pData();
        $chartData->loadPalette("modules/AOR_Charts/lib/pChart/palettes/navy.color", true);
        $labels = array();
        foreach ($reportData as $row) {
            $chartData->addPoints($row[$yName], 'data');
            $chartData->addPoints($row[$xName], 'Labels');
            $labels[] = $row[$xName];
        }

        $chartData->setSerieDescription("Months", "Month");
        $chartData->setAbscissa("Labels");

        $imageHeight = 700;
        $imageWidth = 700;

        $chartPicture = new pImage($imageWidth, $imageHeight, $chartData);
        if ($generateImageMapId) {
            $imageMapDir = create_cache_directory('modules/AOR_Charts/ImageMap/'.$current_user->id.'/');
            $chartPicture->initialiseImageMap($generateImageMapId, IMAGE_MAP_STORAGE_FILE, $generateImageMapId, $imageMapDir);
        }

        $chartPicture->Antialias = true;

        $chartPicture->drawFilledRectangle(0, 0, $imageWidth-1, $imageHeight-1, array("R"=>240,"G"=>240,"B"=>240,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0,));

        $chartPicture->setFontProperties(array("FontName"=>"modules/AOR_Charts/lib/pChart/fonts/verdana.ttf","FontSize"=>14));

        $chartPicture->drawText($imageWidth/2, 20, $this->name, array("R"=>0,"G"=>0,"B"=>0,'Align'=>TEXT_ALIGN_TOPMIDDLE));
        $chartPicture->setFontProperties(array("FontName"=>"modules/AOR_Charts/lib/pChart/fonts/verdana.ttf","FontSize"=>6));

        $chartPicture->setGraphArea(60, 60, $imageWidth-60, $imageHeight-100);

        switch ($this->type) {
            case 'radar':
                $this->buildChartImageRadar($chartPicture, $chartData, !empty($generateImageMapId));
                break;
            case 'pie':
                $this->buildChartImagePie($chartPicture, $chartData, $reportData, $imageHeight, $imageWidth, $xName, !empty($generateImageMapId));
                break;
            case 'line':
                $this->buildChartImageLine($chartPicture, !empty($generateImageMapId));
                break;
            case 'bar':
            default:
                $this->buildChartImageBar($chartPicture, !empty($generateImageMapId));
                break;
        }
        if ($generateImageMapId) {
            $chartPicture->replaceImageMapTitle("data", $labels);
        }
        ob_start();
        $chartPicture->render(null);
        $img = ob_get_clean();
        if ($asDataURI) {
            return 'data:image/png;base64,'.base64_encode($img);
        }
        return $img;
    }

    public function buildChartHTML(array $reportData, array $fields, $index = 0, $chartType = AOR_Report::CHART_TYPE_PCHART, AOR_Field $mainGroupField = null)
    {
        switch ($chartType) {
            case AOR_Report::CHART_TYPE_PCHART:
                return $this->buildChartHTMLPChart($reportData, $fields, $index);
            case AOR_Report::CHART_TYPE_CHARTJS:
                return $this->buildChartHTMLChartJS($reportData, $fields);
            case AOR_Report::CHART_TYPE_RGRAPH:
                return $this->buildChartHTMLRGraph($reportData, $fields, $mainGroupField);
        }
        return '';
    }

    /**
     *
     * @param type $labels
     * @return boolean Returns TRUE if colours successfully changed. FALSE on error and using colour defaults.
     */
    private function generateChartColoursFromLabels($labels)
    {
        if (is_array($labels) && count($labels) > 1) {
            $colours = [];
            foreach ($labels as $onelabel) {
                $hash = md5($onelabel);
                $colours[] = substr($hash, 0, 6);
            }
            $this->colours = "['#" . implode("','#", $colours) . "']";
            return true;
        }
        LoggerManager::getLogger()->warn('Incorrect labels given. Using default colours in charts.');
        $this->colours = self::COLOUR_DEFAULTS;
        return false;
    }

    private function buildChartHTMLRGraph(array $reportData, array $fields, AOR_Field $mainGroupField = null)
    {
        $html = '';
        if (!in_array($this->type, $this->getValidChartTypes())) {
            return $html;
        }
        $x = $fields[$this->x_field];
        $y = $fields[$this->y_field];
        if (!$x || !$y) {
            //Malformed chart object - missing an axis field
            return '';
        }
        $xName = str_replace(' ', '_', (string) $x->label) . $this->x_field;
        $yName = str_replace(' ', '_', (string) $y->label) . $this->y_field;

        $defaultHeight = 500;
        $defaultWidth = 900;

        switch ($this->type) {
            /*
              //Polar was not implemented for the previous library (it is not in the getValidChartTypes method)
              case 'polar':
              $chartFunction = 'PolarArea';
              $data = $this->getPolarChartData($reportData, $xName,$yName);
              $config = $this->getPolarChartConfig();
              break;
             */
            case 'radar':
                $chartFunction = 'Radar';
                $data = $this->getRGraphBarChartData($reportData, $xName, $yName);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getRadarChartConfig();
                $chart = $this->getRGraphRadarChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth);
                break;
            case 'pie':
                $chartFunction = 'Pie';
                $data = $this->getRGraphBarChartData($reportData, $xName, $yName);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getPieChartConfig();
                $chart = $this->getRGraphPieChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth);
                break;
            case 'line':
                $chartFunction = 'Line';
                $data = $this->getRGraphBarChartData($reportData, $xName, $yName);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getLineChartConfig();
                $chart = $this->getRGraphLineChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth);
                break;
            case 'rose':
                $chartFunction = 'Rose';
                $data = $this->getRGraphBarChartData($reportData, $xName, $yName);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getRoseChartConfig();
                $chart = $this->getRGraphRoseChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth);
                break;
            case 'grouped_bar':
                $chartFunction = 'Grouped bar';
                $data = $this->getRGraphGroupedBarChartData($reportData, $xName, $yName, $mainGroupField);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getGroupedBarChartConfig();
                $chart = $this->getRGraphGroupedBarChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth, true);
                break;
            case 'stacked_bar':
                $chartFunction = 'Stacked bar';
                $data = $this->getRGraphGroupedBarChartData($reportData, $xName, $yName, $mainGroupField);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getStackedBarChartConfig();
                $chart = $this->getRGraphGroupedBarChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth, false);
                break;
            case 'bar':
            default:
                $chartFunction = 'Bar';
                $data = $this->getRGraphBarChartData($reportData, $xName, $yName);
                $valid = $this->generateChartColoursFromLabels($data['labels']);
                $config = $this->getBarChartConfig();
                $chart = $this->getRGraphBarChart(json_encode($data['data']), json_encode($data['labels']), json_encode($data['tooltips']), $this->name, $this->id, $defaultHeight, $defaultWidth);
                break;
        }

        if (!$valid) {
            // exception
            LoggerManager::getLogger()->error("Invalid char data labels detected for chart type: $this->type");
        }

        return $chart;
    }

    private function getRGraphRoseChart($chartDataValues, $chartLabelValues, $chartTooltips, $chartName, $chartId, $chartHeight = 400, $chartWidth = 400)
    {
        $dataArray = json_decode((string) $chartDataValues);
        if (!is_array($dataArray)||count($dataArray) < 1) {
            return "<h3>$this->noDataMessage</h3>";
        }
        $html = '';
        $html .= "<canvas id='$chartId' width='$chartWidth' height='$chartHeight' class='resizableCanvas'></canvas>";
        $html .= <<<EOF
        <script>
            new RGraph.Rose({
            id: '$chartId',
            options:{
                //title: '$chartName',
                //labels: $chartLabelValues,
                //textSize:8,
                textSize:10,
                //titleSize:10,
                 tooltips:$chartTooltips,
                tooltipsEvent:'onmousemove',
                tooltipsCssClass: 'rgraph_chart_tooltips_css',
                colors: $this->colours,
                colorsSequential:true
            },
            data: $chartDataValues
        }).draw();
        </script>
EOF;
        return $html;
    }



    //I have not used a parameter for getRGraphBarChart to say whether to group etc, as the future development could be quite different
    //for both, hence the separate methods.  However, the $grouped parameter allows us to specify whether the chart is grouped (true)
    //or stacked (false)
    private function getRGraphGroupedBarChart($chartDataValues, $chartLabelValues, $chartTooltips, $chartName, $chartId, $chartHeight = 400, $chartWidth = 400, $grouped = false)
    {
        $dataArray = json_decode((string) $chartDataValues);
        $grouping = 'grouped'; //$mainGroupField->label; //'grouped';
        if (!$grouped) {
            $grouping='stacked';
        }
        if (!is_array($dataArray)||count($dataArray) < 1) {
            return "<h3>$this->noDataMessage</h3>";
        }
        $html = '';
        $html .= "<canvas id='$chartId' width='$chartWidth' height='$chartHeight' class='resizableCanvas'></canvas>";
        $html .= <<<EOF
        <script>
            new RGraph.Bar({
            id: '$chartId',
            data: $chartDataValues,
            options: {
                grouping:'$grouping',
                backgroundGrid:false,
                backgroundGrid:false,
                gutterBottom: 150,
                gutterTop:25,
                gutterLeft:128,
                title: '$chartName',

                tooltips:$chartTooltips,
                tooltipsEvent:'onmousemove',
                tooltipsCssClass: 'rgraph_chart_tooltips_css',

                shadow:false,
                titleSize:10,
                labels: $chartLabelValues,
                textSize:10,
                textAngle: 90,
                colors: $this->colours,
                ymax:calculateMaxYForSmallNumbers($chartDataValues)
            }
        }).draw();
        </script>
EOF;
        return $html;
    }



    private function getRGraphBarChart($chartDataValues, $chartLabelValues, $chartTooltips, $chartName, $chartId, $chartHeight = 400, $chartWidth = 400)
    {
        $dataArray = json_decode((string) $chartDataValues);
        if (!is_array($dataArray)||count($dataArray) < 1) {
            return "<h3>$this->noDataMessage</h3>";
        }
        $html = '';
        $html .= "<canvas id='$chartId' width='$chartWidth' height='$chartHeight' class='resizableCanvas'></canvas>";
        $html .= <<<EOF
        <script>
            new RGraph.Bar({
            id: '$chartId',
            data: $chartDataValues,
            options: {
            title: '$chartName',
                gutterBottom: 150,
                gutterLeft: 128,
                gutterTop: 25,
                //title: '$chartName',
                labels: $chartLabelValues,
                colorsSequential:true,
                textAngle: 90,
                textSize:10,
                titleSize:10,
                backgroundGrid:false,

                tooltips:$chartTooltips,
                tooltipsCssClass: 'rgraph_chart_tooltips_css',
                tooltipsEvent:'onmousemove',

                colors: $this->colours,
                ymax:calculateMaxYForSmallNumbers($chartDataValues)
            }
        }).draw();
        </script>
EOF;
        return $html;
    }

    private function getRGraphRadarChart($chartDataValues, $chartLabelValues, $chartTooltips, $chartName, $chartId, $chartHeight = 400, $chartWidth = 400)
    {
        $dataArray = json_decode((string) $chartDataValues);
        if (!is_array($dataArray)||count($dataArray) < 1) {
            return "<h3>$this->noDataMessage</h3>";
        }
        $html = '';
        $html .= "<canvas id='$chartId' width='$chartWidth' height='$chartHeight' class='resizableCanvas'></canvas>";
        $html .= <<<EOF
        <script>
            new RGraph.Radar({
            id: '$chartId',
            data: $chartDataValues,
            options: {
                title: '$chartName',
                labels: $chartLabelValues,
                textSize:10,


                tooltips:$chartTooltips,
                tooltipsEvent:'onmousemove',
                tooltipsCssClass: 'rgraph_chart_tooltips_css',

                colors: $this->colours,
                ymax:calculateMaxYForSmallNumbers($chartDataValues)
            }
        }).draw();
        </script>
EOF;
        return $html;
    }

    private function getRGraphPieChart($chartDataValues, $chartLabelValues, $chartTooltips, $chartName, $chartId, $chartHeight = 400, $chartWidth = 400)
    {
        $dataArray = json_decode((string) $chartDataValues);
        if (!is_array($dataArray)||count($dataArray) < 1) {
            return "<h3>$this->noDataMessage</h3>";
        }
        /*
                if($chartHeight > 400)
                    $chartHeight = 400;
                if($chartWidth > 600)
                    $chartWidth = 400;
        */
        $html = '';
        $html .= "<canvas id='$chartId' width='$chartWidth' height='$chartHeight' class='resizableCanvas'></canvas>";
        $html .= <<<EOF
        <script>
            new RGraph.Pie({
            id: '$chartId',
            data: $chartDataValues,
            options: {
                title: '$chartName',
                textSize:10,
                titleSize:10,
                 tooltips:$chartTooltips,
                tooltipsEvent:'onmousemove',
                tooltipsCssClass: 'rgraph_chart_tooltips_css',
                labels: $chartLabelValues,
                colors: $this->colours
            }
        }).draw();
        </script>
EOF;
        return $html;
    }

    private function getRGraphLineChart($chartDataValues, $chartLabelValues, $chartTooltips, $chartName, $chartId, $chartHeight = 400, $chartWidth = 400)
    {
        $dataArray = json_decode((string) $chartDataValues);
        if (!is_array($dataArray)||count($dataArray) < 1) {
            return "<h3>$this->noDataMessage</h3>";
        }
        $html = '';
        $html .= "<canvas id='$chartId' width='$chartWidth' height='$chartHeight' class='resizableCanvas'></canvas>";
        $html .= <<<EOF
        <script>
            new RGraph.Line({
            id: '$chartId',
            data: $chartDataValues,
            options: {
                title: '$chartName',
                gutterBottom: 150,
                //gutterTop:50,
                tickmarks:'encircle',
                textSize:10,
                titleSize:10,
                gutterLeft:128,
                //title: '$chartName',
                labels: $chartLabelValues,

                 tooltips:$chartTooltips,
                tooltipsEvent:'onmousemove',
                tooltipsCssClass: 'rgraph_chart_tooltips_css',

                tickmarks:'circle',

                textAngle: 90,
                //titleSize:10,
                backgroundGrid:false,
                colors: $this->colours,
                ymax:calculateMaxYForSmallNumbers($chartDataValues),
            }
        }).draw();
        </script>
EOF;
        return $html;
    }

    private function buildChartHTMLChartJS(array $reportData, array $fields)
    {
        $html = '';
        if (!in_array($this->type, $this->getValidChartTypes())) {
            return $html;
        }
        $x = $fields[$this->x_field];
        $y = $fields[$this->y_field];
        if (!$x || !$y) {
            //Malformed chart object - missing an axis field
            return '';
        }
        $xName = str_replace(' ', '_', (string) $x->label) . $this->x_field;
        $yName = str_replace(' ', '_', (string) $y->label) . $this->y_field;

        switch ($this->type) {
            case 'radar':
                $chartFunction = 'Radar';
                $data = $this->getRadarChartData($reportData, $xName, $yName);
                $config = $this->getRadarChartConfig();
                break;
            case 'pie':
                $chartFunction = 'Pie';
                $data = $this->getPieChartData($reportData, $xName, $yName);
                $config = $this->getPieChartConfig();
                break;
            case 'line':
                $chartFunction = 'Line';
                $data = $this->getLineChartData($reportData, $xName, $yName);
                $config = $this->getLineChartConfig();
                break;
            case 'bar':
            default:
                $chartFunction = 'Bar';
                $data = $this->getBarChartData($reportData, $xName, $yName);
                $config = $this->getBarChartConfig();
                break;
        }
        $data = json_encode($data);
        $config = json_encode($config);
        $chartId = 'chart'.$this->id;
        $html .= "<h3>{$this->name}</h3>";
        $html .= "<canvas id='{$chartId}' width='400' height='400'></canvas>";
        $html .= <<<EOF
        <script>
        $(document).ready(function(){
            SUGAR.util.doWhen("typeof Chart != 'undefined'", function(){
                var data = {$data};
                var ctx = document.getElementById("{$chartId}").getContext("2d");
                console.log('Creating new chart');
                var config = {$config};
                var chart = new Chart(ctx).{$chartFunction}(data, config);
                var legend = chart.generateLegend();
                $('#{$chartId}').after(legend);
            });
        });
        </script>
EOF;
        return $html;
    }

    private function buildChartHTMLPChart(array $reportData, array $fields, $index = 0)
    {
        $html = '';
        $imgUri = $this->buildChartImage($reportData, $fields, true, $index);
        $img = "<img id='{$this->id}_img' src='{$imgUri}'>";
        $html .= $img;
        $html .= <<<EOF
<script>
SUGAR.util.doWhen("typeof addImage != 'undefined'", function(){
    addImage('{$this->id}_img','{$this->id}_img_map','index.php?module=AOR_Charts&action=getImageMap&to_pdf=1&imageMapId={$index}');
});
</script>
EOF;
        return $html;
    }

    private function getShortenedLabel($label, $maxLabelSize = 20)
    {
        if (strlen((string) $label) > $maxLabelSize) {
            return mb_substr($label, 0, $maxLabelSize).'...';
        }
        return $label;
    }


    private function getRGraphGroupedBarChartData($reportData, $xName, $yName, AOR_Field $mainGroupField = null)
    {


        // get z-axis name

        $zName = null;
        foreach ($reportData[0] as $key => $value) {
            $field = str_replace(' ', '_', is_null($mainGroupField) ? 'no data' : $mainGroupField->label);
            if (preg_match('/^' . $field . '[0-9]+/', (string) $key)) {
                $zName = $key;
                break;
            }
        }



        // get grouped values

        $data = array();
        $tooltips = array();

        $usedKeys = array();
        foreach ($reportData as $key => $row) {
            $filter = $row[$xName];
            foreach ($reportData as $key2 => $row2) {
                if ($row2[$xName] == $filter && !in_array($key, $usedKeys)) {
                    $data      [ $row[$xName]  ]   [] = (float) $row[$yName];
                    $tooltips  [ $row[$xName]  ]   [] = isset($row[$zName]) ? $row[$zName] : null;
                    $usedKeys[] = $key;
                }
            }
        }

        $_data = array();
        foreach ($data as $label => $values) {
            $_data[$label] = [];
            foreach ($values as $key => $value) {
                $_data[$label][$tooltips[$label][$key]] = ($_data[$label][$tooltips[$label][$key]] ?? 0) + 1;
            }
        }

        $data = $_data;


        // make data format for charts

        $_data = array();
        $_labels = array();
        $_tooltips = array();
        foreach ($data as $label => $values) {
            $_labels[] = $this->getShortenedLabel($label);
            $_values = array();
            foreach ($values as $tooltip => $value) {
                $_tooltips[] = $tooltip . " ($value)";
                $_values[] = $value;
            }
            $_data[] = $_values;
        }


        $chart = array(
            'data' => $_data,
            'labels' => $_labels,
            'tooltips' => $_tooltips,
        );

        return $chart;
    }

    private function getRGraphBarChartData($reportData, $xName, $yName)
    {
        $chart = [];
        $chart['labels']=array();
        $chart['data']=array();
        $chart['tooltips']=array();
        foreach ($reportData as $row) {
            $chart['labels'][] = $this->getShortenedLabel($row[$xName]);
            $chart['tooltips'][] = $row[$xName].': '.$row[$yName];
            $chart['data'][] = (float)$row[$yName];
        }
        return $chart;
    }


    private function getBarChartData($reportData, $xName, $yName)
    {
        $data = array();
        $data['labels'] = array();
        $datasetData = array();
        foreach ($reportData as $row) {
            $data['labels'][] = $row[$xName];
            $datasetData[] = $row[$yName];
        }

        $data['datasets'] = array();
        $data['datasets'][] = array(
            'fillColor' => "rgba(151,187,205,0.2)",
            'strokeColor' => "rgba(151,187,205,1)",
            'pointColor' => "rgba(151,187,205,1)",
            'pointStrokeColor' => "#fff",
            'pointHighlightFill' => "#fff",
            'pointHighlightStroke' => "rgba(151,187,205,1)4",
            'data'=>$datasetData);
        return $data;
    }

    private function getLineChartData($reportData, $xName, $yName)
    {
        return $this->getBarChartData($reportData, $xName, $yName);
    }

    private function getBarChartConfig()
    {
        return array();
    }
    private function getLineChartConfig()
    {
        return $this->getBarChartConfig();
    }

    private function getGroupedBarChartConfig()
    {
        return $this->getBarChartConfig();
    }

    private function getStackedBarChartConfig()
    {
        return $this->getBarChartConfig();
    }

    private function getRoseChartConfig()
    {
        return $this->getBarChartConfig();
    }

    private function getRadarChartData($reportData, $xName, $yName)
    {
        return $this->getBarChartData($reportData, $xName, $yName);
    }

    private function getRadarChartConfig()
    {
        return array();
    }

    private function getPieChartConfig()
    {
        $config = array();
        $config['legendTemplate'] = "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\">&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;<%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>";
        return $config;
    }

    private function getPieChartData($reportData, $xName, $yName)
    {
        $data = array();

        foreach ($reportData as $row) {
            if (!$row[$yName]) {
                continue;
            }
            $colour = $this->getColour($row[$xName]);
            $data[] = array(
                'value' => (int)$row[$yName],
                'label' => $row[$xName],
                'color' => $colour['main'],
                'highlight' => $colour['highlight'],
            );
        }
        return $data;
    }
}