2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 /*********************************************************************************
41 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
42 * Reserved. Contributor(s): ______________________________________..
43 *********************************************************************************/
49 var $forceHideDataGroupLink = false;
50 var $data_set = array();
51 var $display_data = array();
52 var $chart_properties = array();
53 var $chart_yAxis = array();
54 var $group_by = array();
55 var $super_set = array();
56 var $colors_list = array();
57 var $base_url = array();
58 var $url_params = array();
61 var $thousands_symbol;
64 public function __construct() {
65 $this->db = &DBManagerFactory::getInstance();
66 $this->ss = new Sugar_Smarty();
68 $this->chart_yAxis['yMin'] = 0;
69 $this->chart_yAxis['yMax'] = 0;
72 if ($GLOBALS['current_user']->getPreference('currency')){
74 $currency = new Currency();
75 $currency->retrieve($GLOBALS['current_user']->getPreference('currency'));
76 $this->div = $currency->conversion_rate;
77 $this->currency_symbol = $currency->symbol;
80 $this->currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
82 $this->is_currency = false;
86 function getData($query){
87 $result = $this->db->query($query);
89 $row = $this->db->fetchByAssoc($result);
92 $this->data_set[] = $row;
93 $row = $this->db->fetchByAssoc($result);
97 function constructBaseURL(){
101 foreach ($this->base_url as $param => $value){
102 if ($numParams == 0){
103 $url .= $param . '=' . $value;
106 $url .= '&' .$param . '=' .$value;
114 function constructURL(){
115 $url = $this->constructBaseURL();
116 foreach ($this->url_params as $param => $value){
117 if ($param == 'assigned_user_id') $param = 'assigned_user_id[]';
118 if (is_array($value)){
119 foreach($value as $multiple){
120 $url .= '&' . $param . '=' . urlencode($multiple);
124 $url .= '&' . $param . '=' . urlencode($value);
130 function setData($dataSet){
131 $this->data_set = $dataSet;
134 function setProperties($title, $subtitle, $type, $legend='on', $labels='value', $print='on'){
135 $this->chart_properties['title'] = $title;
136 $this->chart_properties['subtitle'] = $subtitle;
137 $this->chart_properties['type'] = $type;
138 $this->chart_properties['legend'] = $legend;
139 $this->chart_properties['labels'] = $labels;
142 function setDisplayProperty($property, $value){
143 $this->chart_properties[$property] = $value;
146 function setColors($colors = array()){
147 $this->colors_list = $colors;
151 * returns the header for the constructed xml file for sugarcharts
154 * @return string $header XML header
156 function xmlHeader(){
157 $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
158 $header .= "<sugarcharts version=\"1.0\">\n";
164 * returns the footer for the constructed xml file for sugarcharts
167 * @return string $footer XML footer
169 function xmlFooter(){
170 $footer = "</sugarcharts>";
176 * returns the properties tag for the constructed xml file for sugarcharts
179 * @return string $properties XML properties tag
181 function xmlProperties(){
182 // open the properties tag
183 $properties = $this->tab("<properties>",1);
185 // grab the property and value from the chart_properties variable
186 foreach ($this->chart_properties as $key => $value){
187 $properties .= $this->tab("<$key>$value</$key>",2);
190 if (!empty($this->colors_list)){
191 // open the colors tag
192 $properties .= $this->tab("<colors>",2);
193 foreach ($this->colors_list as $color){
194 $properties .= $this->tab("<color>$color</color>",3);
197 // close the colors tag
198 $properties .= $this->tab("</colors>",2);
201 // close the properties tag
202 $properties .= $this->tab("</properties>",1);
208 * returns the y-axis values for the chart
211 * @return string $yAxis XML yAxis tag
214 $this->chart_yAxis['yStep'] = '100';
215 $this->chart_yAxis['yLog'] = '1';
216 $this->chart_yAxis['yMax'] = $this->is_currency ? $this->convertCurrency($this->chart_yAxis['yMax']) : $this->chart_yAxis['yMax'];
217 $max = $this->chart_yAxis['yMax'];
218 $exp = ($max == 0) ? 1 : floor(log10($max));
219 $baseval = $max / pow(10, $exp);
221 // steps will be 10^n, 2*10^n, 5*10^n (where n >= 0)
222 if ($baseval > 0 && $baseval <= 1){
223 $step = 2 * pow(10, $exp-1);
225 else if ($baseval > 1 && $baseval <= 3){
226 $step = 5 * pow(10, $exp-1);
228 else if ($baseval > 3 && $baseval <= 6){
229 $step = 10 * pow(10, $exp-1);
231 else if ($baseval > 6 && $baseval <= 10){
232 $step = 20 * pow(10, $exp-1);
235 // edge cases for values less than 10
236 if ($max == 0 || $step < 1){
240 $this->chart_yAxis['yStep'] = $step;
242 // to compensate, the yMax should be at least one step above the max value
243 $this->chart_yAxis['yMax'] += $this->chart_yAxis['yStep'];
245 $yAxis = $this->tab("<yAxis>" ,1);
247 foreach ($this->chart_yAxis as $key => $value){
248 $yAxis .= $this->tab("<$key>$value</$key>", 2);
251 $yAxis .= $this->tab("</yAxis>" ,1);
257 * returns the total amount value for the group by field
259 * @param group by field
260 * @return int $total total value
262 function calculateTotal($group_by){
265 for($i =0; $i < count($this->data_set); $i++){
266 if ($this->data_set[$i][$this->group_by[0]] == $group_by){
267 $total += $this->data_set[$i]['total'];
274 * returns text with tabs appended before it
276 * @param string $str input string
277 * int $depth number of times to tab
278 * @return string with tabs appended before it
280 function tab($str, $depth){
281 return str_repeat("\t", $depth) . $str . "\n";
285 * returns xml data format
288 * @return string with xml data format
290 function processData(){
293 $group_by = $this->group_by[0];
294 if (isset($this->group_by[1])){
295 $drill_down = $this->group_by[1];
300 for($i =0; $i < count($this->data_set); $i++){
301 if ($this->data_set[$i][$group_by] != $prev_group_by){
302 $prev_group_by = $this->data_set[$i][$group_by];
303 $data[$this->data_set[$i][$group_by]] = array();
306 $data[$this->data_set[$i][$group_by]][] = $this->data_set[$i];
308 // push new item onto legend items list
309 if (isset($drill_down)){
310 if (!in_array($this->data_set[$i][$drill_down], $this->super_set)){
311 $this->super_set[] = $this->data_set[$i][$drill_down];
319 function processDataGroup($tablevel, $title, $value, $label, $link){
320 $link = $this->forceHideDataGroupLink ? '' : $link;
321 $data = $this->tab('<group>',$tablevel);
322 $data .= $this->tab('<title>' . $title . '</title>',$tablevel+1);
323 $data .= $this->tab('<value>' . $value . '</value>',$tablevel+1);
324 $data .= $this->tab('<label>' . $label . '</label>',$tablevel+1);
325 $data .= $this->tab('<link>' . $link . '</link>',$tablevel+1);
326 $data .= $this->tab('</group>',$tablevel);
330 function calculateGroupByTotal($dataset){
333 foreach ($dataset as $key => $value){
340 function calculateSingleBarMax($dataset){
342 foreach ($dataset as $value){
352 * returns correct yAxis min/max
354 * @param value to check
355 * @return yAxis min and max
357 function checkYAxis($value){
358 if ($value < $this->chart_yAxis['yMin']){
359 $this->chart_yAxis['yMin'] = $value;
361 else if ($value > $this->chart_yAxis['yMax']){
362 $this->chart_yAxis['yMax'] = $value;
367 function convertCurrency($to_convert){
370 $decimals = $locale->getPrecision();
371 $amount = ($this->div == 1) ? $to_convert : round($to_convert * $this->div,$decimals);
376 function formatNumber($number, $decimals= null, $decimal_point= null, $thousands_sep= null){
378 if(is_null($decimals)) {
379 $decimals = $locale->getPrecision();
381 $seps = get_number_seperators();
382 $thousands_sep = $seps[0];
383 $decimal_point = $seps[1];
384 return number_format($number, $decimals, $decimal_point, $thousands_sep);
388 $new_data = $this->processData();
390 foreach ($new_data as $groupByKey => $value){
391 $total += $this->calculateTotal($groupByKey);
397 function xmlDataForGroupByChart(){
399 foreach ($this->data_set as $key => $value){
400 $amount = $this->is_currency ? $this->convertCurrency($this->calculateGroupByTotal($value)) : $this->calculateGroupByTotal($value);
401 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
403 $data .= $this->tab('<group>',2);
404 $data .= $this->tab('<title>' . $key . '</title>',3);
405 $data .= $this->tab('<value>' . $amount . '</value>',3);
406 $data .= $this->tab('<label>' . $label . '</label>',3);
407 $data .= $this->tab('<link></link>',3);
408 $data .= $this->tab('<subgroups>',3);
410 foreach ($value as $k => $v){
411 $amount = $this->is_currency ? $this->convertCurrency($v) : $v;
412 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount)) : $amount;
414 $data .= $this->tab('<group>',4);
415 $data .= $this->tab('<title>' . $k . '</title>',5);
416 $data .= $this->tab('<value>' . $amount . '</value>',5);
417 $data .= $this->tab('<label>' . $label . '</label>',5);
418 $data .= $this->tab('<link></link>',5);
419 $data .= $this->tab('</group>',4);
420 $this->checkYAxis($v);
422 $data .= $this->tab('</subgroups>',3);
423 $data .= $this->tab('</group>',2);
429 function xmlDataForGaugeChart(){
431 $gaugePosition = $this->data_set[0]['num'];
432 $this->chart_yAxis['yMax'] = $this->chart_properties['gaugeTarget'];
433 $this->chart_yAxis['yStep'] = 1;
434 $data .= $this->processDataGroup(2, 'GaugePosition', $gaugePosition, $gaugePosition, '');
435 $data .= $this->processGauge($gaugePosition, $this->chart_properties['gaugeTarget']);
440 function xmlDataBarChart(){
442 $max = $this->calculateSingleBarMax($this->data_set);
443 $this->checkYAxis($max);
445 if (isset($this->group_by[0])){
446 $group_by = $this->group_by[0];
447 if (isset($this->group_by[1])){
448 $drill_down = $this->group_by[1];
452 foreach ($this->data_set as $key => $value){
453 if ($this->is_currency){
454 $value = $this->convertCurrency($value);
455 $label = $this->currency_symbol;
456 $label .= $this->formatNumber($value);
457 $label .= $this->thousands_symbol;
463 $data .= $this->tab('<group>', 2);
464 $data .= $this->tab('<title>' . $key . '</title>', 3);
465 $data .= $this->tab('<value>' . $value . '</value>', 3);
466 $data .= $this->tab('<label>' . $label . '</label>', 3);
467 if (isset($drill_down) && $drill_down){
468 if ($this->group_by[0] == 'm'){
469 $additional_param = '&date_closed_advanced=' . urlencode($key);
472 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($key);
474 $url = $this->constructURL() . $additional_param;
476 $data .= $this->tab('<link>' . $url . '</link>', 3);
478 $data .= $this->tab('<subgroups>', 3);
479 $data .= $this->tab('</subgroups>', 3);
480 $data .= $this->tab('</group>', 2);
485 function xmlDataGenericChart(){
487 $group_by = $this->group_by[0];
488 if (isset($this->group_by[1])){
489 $drill_down = $this->group_by[1];
491 $new_data = $this->processData();
493 foreach ($new_data as $groupByKey => $value){
494 $total = $this->calculateTotal($groupByKey);
495 $this->checkYAxis($total);
497 if ($this->group_by[0] == 'm'){
498 $additional_param = '&date_closed_advanced=' . urlencode($groupByKey);
501 $paramValue = (isset($value[0]['key']) && $value[0]['key'] != '') ? $value[0]['key'] : $groupByKey;
502 $paramValue = (isset($value[0][$this->group_by[0]."_dom_option"]) && $value[0][$this->group_by[0]."_dom_option"] != '') ? $value[0][$this->group_by[0]."_dom_option"] : $paramValue;
503 $additional_param = "&" . $this->group_by[0] . "=" . urlencode($paramValue);
506 $url = $this->constructURL() . $additional_param;
508 $amount = $this->is_currency ? $this->convertCurrency($total) : $total;
509 $label = $this->is_currency ? ($this->currency_symbol . $this->formatNumber($amount) . 'K') : $amount;
511 $data .= $this->tab('<group>',2);
512 $data .= $this->tab('<title>' . $groupByKey . '</title>',3);
513 $data .= $this->tab('<value>' . $amount . '</value>',3);
514 $data .= $this->tab('<label>' . $label . '</label>',3);
515 $data .= $this->tab('<link>' . $url . '</link>',3);
517 $data .= $this->tab('<subgroups>',3);
518 $processed = array();
520 if (isset($drill_down) && $drill_down != ''){
521 for($i =0; $i < count($new_data[$groupByKey]); $i++){
522 if ($new_data[$groupByKey][$i][$group_by] == $groupByKey){
523 if ($drill_down == 'user_name'){
524 $drill_down_param = '&assigned_user_id[]=' . urlencode($new_data[$groupByKey][$i]['assigned_user_id']);
526 else if ($drill_down == 'm'){
527 $drill_down_param = '&date_closed_advanced=' . urlencode($new_data[$groupByKey][$i][$drill_down]);
530 $paramValue = (isset($new_data[$groupByKey][$i][$drill_down."_dom_option"]) && $new_data[$groupByKey][$i][$drill_down."_dom_option"] != '') ? $new_data[$groupByKey][$i][$drill_down."_dom_option"] : $new_data[$groupByKey][$i][$drill_down];
531 $drill_down_param = '&' . $drill_down . '=' . urlencode($paramValue);
534 if($this->is_currency) {
535 $sub_amount = $this->formatNumber($this->convertCurrency($new_data[$groupByKey][$i]['total']));
536 $sub_amount_formatted = $this->currency_symbol . $sub_amount . 'K';
538 $sub_amount = $new_data[$groupByKey][$i]['total'];
539 $sub_amount_formatted = $sub_amount;
542 $data .= $this->processDataGroup(4, $new_data[$groupByKey][$i][$drill_down],
544 $sub_amount_formatted,
545 $url . $drill_down_param );
546 array_push($processed, $new_data[$groupByKey][$i][$drill_down]);
549 $not_processed = array_diff($this->super_set, $processed);
550 foreach ($not_processed as $title){
551 $data .= $this->processDataGroup(4, $title, 'NULL', '', $url);
555 $data .= $this->tab('</subgroups>',3);
556 $data .= $this->tab('</group>',2);
562 * returns a name for the XML File
564 * @param string $file_id - unique id to make part of the file name
566 public static function getXMLFileName(
570 global $sugar_config, $current_user;
572 $filename = $sugar_config['tmp_dir']. $current_user->id . '_' . $file_id . '.xml';
574 if ( !is_dir(dirname($filename)) )
575 create_cache_directory(str_ireplace($GLOBALS['sugar_config']['cache_dir'],"",$filename));
580 public function processXmlData(){
583 if ($this->chart_properties['type'] == 'group by chart'){
584 $data .= $this->xmlDataForGroupByChart();
586 else if ($this->chart_properties['type'] == 'bar chart' || $this->chart_properties['type'] == 'horizontal bar chart'){
587 $data .= $this->xmlDataBarChart();
590 $data .= $this->xmlDataGenericChart();
597 $data = $this->tab('<data>',1);
598 $data .= $this->processXmlData();
599 $data .= $this->tab('</data>',1);
605 * function to generate XML and return it
608 * @return string $xmlContents with xml information
610 function generateXML($xmlDataName = false){
611 $xmlContents = $this->xmlHeader();
612 $xmlContents .= $this->xmlProperties();
613 $xmlContents .= $this->xmlData();
614 $xmlContents .= $this->xmlYAxis();
615 $xmlContents .= $this->xmlFooter();
621 * function to save XML contents into a file
623 * @param string $xmlFilename location of the xml file
624 * string $xmlContents contents of the xml file
625 * @return string boolean denoting whether save has failed
627 function saveXMLFile($xmlFilename,$xmlContents) {
631 $xmlContents = chr(255).chr(254).mb_convert_encoding($xmlContents, 'UTF-16LE', 'UTF-8');
634 if (!$fh = sugar_fopen($xmlFilename, 'w')) {
635 $GLOBALS['log']->debug("Cannot open file ($xmlFilename)");
639 // write the contents to the file
640 if (fwrite($fh,$xmlContents) === FALSE) {
641 $GLOBALS['log']->debug("Cannot write to file ($xmlFilename)");
645 $GLOBALS['log']->debug("Success, wrote ($xmlContents) to file ($xmlFilename)");
652 * generates xml file for Flash charts to use for internationalized instances
654 * @param string $xmlFile location of the XML file to write to
657 function generateChartStrings($xmlFile){
658 global $current_language, $app_list_strings;
660 $chartStringsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
661 $chartStringsXML .= "<sugarlanguage version=\"1.0\">\n";
662 $chartStringsXML .= $this->tab("<charts>",1);
664 if (empty($app_list_strings)) {
665 //set module and application string arrays based upon selected language
666 $app_list_strings = return_app_list_strings_language($current_language);
669 // retrieve the strings defined at include/language/en_us.lang.php
670 foreach ($app_list_strings['chart_strings'] as $tag => $chart_string){
671 $chartStringsXML .= $this->tab("<$tag>$chart_string</$tag>",2);
674 $chartStringsXML .= $this->tab("</charts>",1);
675 $chartStringsXML .= "</sugarlanguage>\n";
677 $this->saveXMLFile($xmlFile, $chartStringsXML);
681 * wrapper function to return the html code containing the chart in a div
683 * @param string $name name of the div
684 * string $xmlFile location of the XML file
685 * string $style optional additional styles for the div
686 * @return string returns the html code through smarty
688 function display($name, $xmlFile, $width='320', $height='480', $resize=false){
691 // generate strings for chart if it does not exist
692 global $current_language, $theme, $sugar_config,$app_strings;
694 $chartStringsXML = $GLOBALS['sugar_config']['tmp_dir'].'chart_strings.' . $current_language .'.lang.xml';
695 if (!file_exists($chartStringsXML)){
696 $this->generateChartStrings($chartStringsXML);
699 $this->ss->assign("chartName", $name);
700 $this->ss->assign("chartXMLFile", $xmlFile);
701 $this->ss->assign("chartStringsXML", $chartStringsXML);
703 // chart styles and color definitions
704 $this->ss->assign("chartStyleCSS", SugarThemeRegistry::current()->getCSSURL('chart.css'));
705 $this->ss->assign("chartColorsXML", SugarThemeRegistry::current()->getImageURL('sugarColors.xml'));
707 $this->ss->assign("width", $width);
708 $this->ss->assign("height", $height);
710 $this->ss->assign("resize", $resize);
711 $this->ss->assign("app_strings", $app_strings);
712 return $this->ss->fetch('include/SugarCharts/tpls/chart.tpl');
717 This function is used for localize all the characters in the Chart. And it can also sort all the dom_values by the sequence defined in the dom, but this may produce a lot of extra empty data in the xml file, when the chart is sorted by two key cols.
718 If the data quantity is large, it maybe a little slow.
719 * @param array $data_set The data get from database
720 string $keycolname1 We will sort by this key first
721 bool $translate1 Whether to trabslate the first column
722 string $keycolname1 We will sort by this key secondly, and it can be null, then it will only sort by the first column.
723 bool $translate1 Whether to trabslate the second column
724 bool $ifsort2 Whether to sort by the second column or just translate the second column.
725 * @return The sorted and translated data.
727 function sortData($data_set, $keycolname1=null, $translate1=false, $keycolname2=null, $translate2=false, $ifsort2=false) {
728 //You can set whether the columns need to be translated or sorted. It the column needn't to be translated, the sorting must be done in SQL, this function will not do the sorting.
729 global $app_list_strings;
730 $sortby1[] = array();
731 foreach ($data_set as $row) {
732 $sortby1[] = $row[$keycolname1];
734 $sortby1 = array_unique($sortby1);
735 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
737 $temp_sortby1 = array();
738 foreach(array_keys($app_list_strings[$keycolname1.'_dom']) as $sortby1_value) {
739 if(in_array($sortby1_value, $sortby1)) {
740 $temp_sortby1[] = $sortby1_value;
743 $sortby1 = $temp_sortby1;
746 //if(isset($sortby1[0]) && $sortby1[0]=='') unset($sortby1[0]);//the beginning of lead_source_dom is blank.
747 if(isset($sortby1[0]) && $sortby1[0]==array()) unset($sortby1[0]);//the beginning of month after search is blank.
749 if($ifsort2==false) $sortby2=array(0);
751 if($keycolname2!=null) {
753 foreach ($data_set as $row) {
754 $sortby2[] = $row[$keycolname2];
756 //The data is from the database, the sorting should be done in the sql. So I will not do the sort here.
757 $sortby2 = array_unique($sortby2);
759 $temp_sortby2 = array();
760 foreach(array_keys($app_list_strings[$keycolname2.'_dom']) as $sortby2_value) {
761 if(in_array($sortby2_value, $sortby2)) {
762 $temp_sortby2[] = $sortby2_value;
765 $sortby2 = $temp_sortby2;
771 foreach($sortby1 as $sort1) {
772 foreach($sortby2 as $sort2) {
774 foreach($data_set as $key => $value){
775 if($value[$keycolname1] == $sort1 && (!$ifsort2 || $value[$keycolname2]== $sort2)) {
777 $value[$keycolname1.'_dom_option'] = $value[$keycolname1];
778 $value[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$value[$keycolname1]];
781 $value[$keycolname2.'_dom_option'] = $value[$keycolname2];
782 $value[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$value[$keycolname2]];
784 array_push($data, $value);
785 unset($data_set[$key]);
789 if($ifsort2 && $a==0) {//Add 0 for sorting by the second column, if the first row doesn't have a certain col, it will fill the column with 0.
794 $val[$keycolname1] = $app_list_strings[$keycolname1.'_dom'][$sort1];
795 $val[$keycolname1.'_dom_option'] = $sort1;
798 $val[$keycolname1] = $sort1;
801 $val[$keycolname2] = $app_list_strings[$keycolname2.'_dom'][$sort2];
802 $val[$keycolname2.'_dom_option'] = $sort2;
804 elseif($keycolname2!=null) {
805 $val[$keycolname2] = $sort2;
807 array_push($data, $val);