/** * Load the Fusion chart lib * * @return string */ public function getFusionchart() { $this->cantTrendLine = array(); $document = JFactory::getDocument(); $params = $this->getParams(); $worker = new FabrikWorker(); $fc_version = $params->get('fusionchart_version', 'free_old'); $free22 = $this->pathBase . 'fusionchart/libs/FusionChartsFree/Code/PHPClass/Includes/FusionCharts_Gen.php'; $pro30 = $this->pathBase . 'fusionchart/libs/FusionCharts/Code/PHPClass/Includes/FusionCharts_Gen.php'; if ($fc_version == 'free_22' && JFile::exists($free22)) { require_once $free22; $document->addScript($this->srcBase . "fusionchart/libs/FusionChartsFree/JSClass/FusionCharts.js"); $fc_swf_path = COM_FABRIK_LIVESITE . $this->srcBase . "fusionchart/libs/FusionChartsFree/Charts/"; } elseif ($fc_version == 'pro_30' && JFile::exists($pro30)) { require_once $pro30; $document->addScript($this->srcBase . "fusionchart/libs/FusionCharts/Charts/FusionCharts.js"); $fc_swf_path = COM_FABRIK_LIVESITE . $this->srcBase . "fusionchart/libs/FusionCharts/Charts/"; } else { require_once $this->pathBase . 'fusionchart/libs/FCclass/FusionCharts_Gen.php'; $document->addScript($this->srcBase . "fusionchart/libs/FCcharts/FusionCharts.js"); $fc_swf_path = COM_FABRIK_LIVESITE . $this->srcBase . "fusionchart/libs/FCcharts/"; } $calc_prefixes = array('sum___', 'avg___', 'med___', 'cnt___'); $calc_prefixmap = array('sum___' => 'sums', 'avg___' => 'avgs', 'med___' => 'medians', 'cnt___' => 'count'); $w = $params->get('fusionchart_width'); $h = $params->get('fusionchart_height'); $chartType = $params->get('fusionchart_type', ''); if ($chartType == '') { throw new InvalidArgumentException('Not chart type selected'); } // Create new chart $this->FC = new FusionCharts($chartType, $w, $h); // If we are pro lets use the new JavaScript Library if ($fc_version === 'pro_30') { $this->FC->setRenderer('javascript'); } // Define path to FC's SWF $this->FC->setSWFPath($fc_swf_path); $this->setChartMessages(); // Setting Param string $strParam = $this->getChartParams(); $label_step_ratios = (array) $params->get('fusion_label_step_ratio'); $x_axis_label = (array) $params->get('fusion_x_axis_label'); $chartElements = (array) $params->get('fusionchart_elementList'); $chartColours = (array) $params->get('fusionchart_elcolour'); $listid = (array) $params->get('fusionchart_table'); $chartCumulatives = (array) $params->get('fusionchart_cumulative'); $elTypes = (array) $params->get('fusionchart_element_type'); $this->setAxisLabels(); $dual_y_parents = $params->get('fusionchart_dual_y_parent'); $chartWheres = (array) $params->get('fusionchart_where'); $limits = (array) $params->get('fusionchart_limit'); $this->c = 0; $gdata = array(); $glabels = array(); $gcolours = array(); $gfills = array(); $this->max = array(); $this->min = array(); $calculationLabels = array(); $calculationData = array(); $calcfound = false; $tmodels = array(); $labelStep = 0; foreach ($listid as $tid) { $this->min[$this->c] = 0; $this->max[$this->c] = 0; if (!array_key_exists($tid, $tmodels)) { $listModel = null; $listModel = JModelLegacy::getInstance('list', 'FabrikFEModel'); $listModel->setId($tid); $tmodels[$tid] = $listModel; } else { $listModel = $tmodels[$tid]; } $table = $listModel->getTable(); $form = $listModel->getForm(); // $$$ hugh - adding plugin query, 2012-02-08 if (array_key_exists($this->c, $chartWheres) && !empty($chartWheres[$this->c])) { $chartWhere = $this->_replaceRequest($chartWheres[$this->c]); $chartWhere = $worker->replaceWithUserData($chartWhere); $listModel->setPluginQueryWhere('fusionchart', $chartWhere); } else { // If no where clause, explicitly clear any previously set clause $listModel->unsetPluginQueryWhere('fusionchart'); } /* $$$ hugh - remove pagination BEFORE calling render(). Otherwise render() applies * session state/defaults when it calls getPagination, which is then returned as a cached * object if we call getPagination after render(). So call it first, then render() will * get our cached pagination, rather than vice versa. */ $limit = (int) FArrayHelper::getValue($limits, $this->c, 0); $listModel->setLimits(0, $limit); $nav = $listModel->getPagination(0, 0, $limit); $listModel->render(); $alldata = $listModel->getData(); $cals = $listModel->getCalculations(); $column = $chartElements[$this->c]; $pref = substr($column, 0, 6); $label = FArrayHelper::getValue($x_axis_label, $this->c, ''); $tmpgdata = array(); $tmpglabels = array(); $colour = array_key_exists($this->c, $chartColours) ? str_replace("#", '', $chartColours[$this->c]) : ''; $gcolours[] = $colour; if (in_array($pref, $calc_prefixes)) { /* you shouldn't mix calculation elements with normal elements when creating the chart * so if ONE calculation element is found we use the calculation data rather than normal element data * this is because a calculation element only generates one value, if want to compare two averages then * they get rendered as tow groups of data and on bar charts this overlays one average over the other, rather than next to it */ $calcfound = true; $column = JString::substr($column, 6); $calckey = $calc_prefixmap[$pref]; $caldata = FArrayHelper::getValue($cals[$calckey], $column . '_obj'); if (is_array($caldata)) { foreach ($caldata as $k => $o) { if ($k !== 'Total') { $calculationData[] = (double) $o->value; $calculationLabels[] = trim(strip_tags($o->label)); } } } if (!empty($calculationData)) { $this->max[$this->c] = max($calculationData); $this->min[$this->c] = min($calculationData); } $gdata[$this->c] = implode(',', $tmpgdata); $glabels[$this->c] = implode('|', $tmpglabels); /* $$$ hugh - playing around with pie charts * $gsums[$this->c] = array_sum($tmpgdata); */ $gsums[$this->c] = array_sum($calculationData); } else { $origColumn = $column; // _raw fields are most likely to contain the value $column = $column . '_raw'; foreach ($alldata as $group) { foreach ($group as $row) { if (!array_key_exists($column, $row)) { // Didn't find a _raw column - revert to orig $column = $origColumn; if (!array_key_exists($column, $row)) { JError::raiseWarning(E_NOTICE, $column . ': NOT FOUND - PLEASE CHECK IT IS PUBLISHED'); continue; } } $tmpgdata[] = trim($row->{$column}) == '' ? -1 : (double) $row->{$column}; $tmpglabels[] = !empty($label) ? html_entity_decode(strip_tags($row->{$label})) : ''; } if (!empty($tmpgdata)) { $this->max[$this->c] = max($tmpgdata); $this->min[$this->c] = min($tmpgdata); } $gdata[$this->c] = implode(',', $tmpgdata); $glabels[$this->c] = implode('|', $tmpglabels); // $$$ hugh - playing around with pie charts $gsums[$this->c] = array_sum($tmpgdata); // $$$ hugh - playing with 'cumulative' option $this->gcumulatives[$this->c] = array(); while (!empty($tmpgdata)) { $this->gcumulatives[$this->c][] = array_sum($tmpgdata); array_pop($tmpgdata); } $this->gcumulatives[$this->c] = array_reverse($this->gcumulatives[$this->c]); } } $this->c++; } if ($calcfound) { $calculationLabels = array_reverse($calculationLabels); $glabels = array(implode('|', array_reverse($calculationLabels))); $gdata = array(implode(',', $calculationData)); } /* $$$ hugh - pie chart data has to be summed - the API only takes a * single dataset for pie charts. And it doesn't make sense trying to * chart individual row data for multiple elements in a pie chart. * Also, labels need to be axisLabels, not $glabels */ switch ($chartType) { // Single Series Charts case 'AREA2D': case 'BAR2D': case 'COLUMN2D': case 'COLUMN3D': case 'DOUGHNUT2D': case 'DOUGHNUT3D': case 'LINE': /* $$$ tom - for now I'm enabling Pie charts here so that it displays * something until we do it properly as you said hugh * Well maybe there's something I don't get but in fact FC already draw * the pies by "percenting" the values of each data... if you know what I mean Hugh;) */ /* $$$ tom - for now I'm enabling Pie charts here so that it displays * something until we do it properly as you said hugh * Well maybe there's something I don't get but in fact FC already draw * the pies by "percenting" the values of each data... if you know what I mean Hugh;) */ case 'PIE2D': case 'PIE3D': case 'SCATTER': // Adding specific params for Pie charts if ($chartType == 'PIE2D' || $chartType == 'PIE3D') { $strParam .= ';pieBorderThickness=' . $params->get('fusionchart_borderthick', ''); $strParam .= ';pieBorderAlpha=' . $params->get('fusionchart_cnvalpha', ''); $strParam .= ';pieFillAlpha=' . FArrayHelper::getValue($params->get('fusionchart_elalpha', array()), 0); } if ($this->c > 1) { $arrCatNames = array(); foreach ($this->axisLabels as $alkey => $al) { $arrCatNames[] = $al; } $arrData = array(); $i = 0; foreach ($gsums as $gd) { $arrData[$i][0] = $this->axisLabels[$i]; $arrData[$i][1] = $gd; $i++; } $this->FC->addChartDataFromArray($arrData, $arrCatNames); } else { // Single table/elements, so use the row data $labels = explode('|', $glabels[0]); // $gsums = !array_key_exists(0, $chartCumulatives) || $chartCumulatives[0] == '0' ? explode(',', $gdata[0]) : explode(',', $gcumulatives[0]); $gsums = FArrayHelper::getValue($chartCumulatives, 0, '0') == '0' ? explode(',', $gdata[0]) : $this->gcumulatives[0]; // Scale to percentages $tot_sum = array_sum($gsums); $arrData = array(); $labelStep = 0; $label_step_ratio = (int) FArrayHelper::getValue($label_step_ratios, 0, 1); if ($label_step_ratio > 1) { $labelStep = (int) (count($gsums) / $label_step_ratio); $strParam .= ';labelStep=' . $labelStep; } /* $$$tom: inverting array_combine as identical values in gsums will be * dropped otherwise. Should I do that differently? * $$$ hugh - can't use array_combine, as empty labels end up dropping values * $arrComb = array_combine($labels, $gsums); * foreach ($arrComb as $key => $value) { */ if ($elTypes[0] == 'trendonly') { $str_params = ''; $min = min($gsums); $max = max($gsums); list($min, $max) = $this->getTrendMinMax($min, $max, 0); $this->FC->addChartData($min, $str_params); $this->FC->addChartData($max, $str_params); } else { $data_count = 0; foreach ($gsums as $key => $value) { $data_count++; if ($value == '-1') { $value = null; } $label = $labels[$key]; $str_params = 'name=' . $label; if ($labelStep) { if ($data_count != 1 && $data_count % $labelStep != 0) { $str_params .= ';showName=0'; } } $this->FC->addChartData($value, $str_params); } } } break; case 'MSBAR2D': case 'MSBAR3D': case 'MSCOLUMN2D': case 'MSCOLUMN3D': case 'MSLINE': case 'MSAREA2D': case 'MSCOMBIDY2D': case 'MULTIAXISLINE': case 'STACKEDAREA2D': case 'STACKEDBAR2D': case 'STACKEDCOLUMN2D': case 'STACKEDCOLUMN3D': case 'SCROLLAREA2D': case 'SCROLLCOLUMN2D': case 'SCROLLLINE2D': case 'SCROLLSTACKEDCOLUMN2D': if ($this->c > 1) { if ($chartType == 'SCROLLAREA2D' || $chartType == 'SCROLLCOLUMN2D' || $chartType == 'SCROLLLINE2D') { $strParam .= ';numVisiblePlot=' . $params->get('fusionchart_scroll_numvisible', 0); } // $$$ hugh - Dual-Y types if ($chartType == 'MSCOMBIDY2D' || $chartType == 'MULTIAXISLINE') { $p_parents = array(); $s_parents = array(); foreach ($dual_y_parents as $dual_y_key => $dual_y_parent) { if ($dual_y_parent == "P") { $p_parents[] = $this->axisLabels[$dual_y_key]; } else { $s_parents[] = $this->axisLabels[$dual_y_key]; } } $strParam .= ';PYAxisName=' . implode(' ', $p_parents); $strParam .= ';SYaxisName=' . implode(' ', $s_parents); } $label_step_ratio = (int) FArrayHelper::getValue($label_step_ratios, 0, 1); if ($label_step_ratio > 1) { $labelStep = (int) (count(explode(',', $gdata[0])) / $label_step_ratio); $strParam .= ';labelStep=' . $labelStep; } // Start tom's changes $labels = explode('|', $glabels[0]); $data_count = 0; foreach ($labels as $catLabel) { $data_count++; $catParams = ''; if ($labelStep) { if ($data_count == 1 || $data_count % $labelStep == 0) { $catParams = 'ShowLabel=1'; } else { $catParams = 'ShowLabel=0'; $catLabel = ''; } } $this->FC->addCategory($catLabel, $catParams); } foreach ($gdata as $key => $chartdata) { $cdata = FArrayHelper::getValue($chartCumulatives, $key, '0') == '0' ? explode(',', $gdata[$key]) : $this->gcumulatives[$key]; $dataset = $this->axisLabels[$key]; $extras = 'parentYAxis=' . $dual_y_parents[$key]; $color = FArrayHelper::getValue($gcolours, $key, ''); if (!empty($color)) { $extras .= ";color=" . $color; } $this->FC->addDataset($dataset, $extras); if ($elTypes[$key] == 'trendonly') { $str_params = ''; $strParam .= ';connectNullData=1'; $min = min($cdata); $max = max($cdata); list($min, $max) = $this->getTrendMinMax($min, $max, $key); $max_datapoints = $this->getMaxDatapoints($gdata); $this->FC->addChartData($min, $str_params); for ($x = 0; $x < $max_datapoints - 2; $x++) { $this->FC->addChartData('', $str_params); } $this->FC->addChartData($max, $str_params); } else { $data_count = 0; foreach ($cdata as $value) { $data_count++; if ($value == '-1') { $value = null; } $this->FC->addChartData($value); } } } } } $this->c > 1 ? $this->trendLine($gdata) : $this->trendLine(); $colours = implode($calcfound ? '|' : ',', $gcolours); // Set chart attributes if ($params->get('fusionchart_custom_attributes', '')) { $strParam .= ';' . trim($params->get('fusionchart_custom_attributes')); } $strParam = "{$strParam}"; $this->FC->setChartParams($strParam); // Render Chart if ($chartType == 'MULTIAXISLINE') { // Nasty, nasty hack for MULTIAXIS, as the FC class doesn't support it. So need to get the chart XML, // split out the <dataset>...</dataset> and wrap them in <axis>...</axis> $axis_attrs = (array) $params->get('fusionchart_mx_attributes'); $dataXML = $this->FC->getXML(); $matches = array(); if (preg_match_all('#(<\\s*dataset[^>]*>.*?<\\s*/dataset\\s*>)#', $dataXML, $matches)) { $index = 0; foreach ($gdata as $key => $chartdata) { $axis = "<axis " . $axis_attrs[$index] . ">" . $matches[0][$index] . "</axis>"; $dataXML = str_replace($matches[0][$index], $axis, $dataXML); $index++; } } return $this->FC->renderChartFromExtXML($dataXML); } else { return $this->FC->renderChart(false, false); } }
/** * iterates through string to replace every * {placeholder} with posted data * @param string text to parse * @param array data to search for placeholders (default $_REQUEST) * @param bool if no data found for the place holder do we keep the {...} string in the message * @param bool add slashed to the text? * @param object user to use in replaceWithUserData (defaults to logged in user) */ public function parseMessageForPlaceHolder($msg, $searchData = null, $keepPlaceholders = true, $addslashes = false, $theirUser = null) { $this->_parseAddSlases = $addslashes; if ($msg == '' || is_array($msg) || strpos($msg, '{') === false) { return $msg; } $msg = str_replace(array('%7B', '%7D'), array('{', '}'), $msg); if (is_object($searchData)) { $searchData = JArrayHelper::fromObject($searchData); } //$post = JRequest::get('post'); //$$$ rob changed to allow request variables to be parsed as well. I have a sneaky feeling this // was set to post for a good reason, but I can't see why now. // $$$ hugh - for reasons I don't understand, merging request just doesn't seem to work // in some situations, so I'm adding a replaceRequest call here as a bandaid. // @TODO $$$ rob can you remember what those situations where? Because doing this is messing up form plugins (e.g redirect) when they do replace on getEmailData() // as having the line below commented in causes the request to be used before searchData. // FabrikWorker::replaceRequest($msg); $post = JRequest::get('request'); $this->_searchData = is_null($searchData) ? $post : array_merge($post, $searchData); $this->_searchData['JUtility::getToken'] = JUtility::getToken(); $msg = FabrikWorker::replaceWithUserData($msg); if (!is_null($theirUser)) { $msg = FabrikWorker::replaceWithUserData($msg, $theirUser, 'your'); } $msg = FabrikWorker::replaceWithGlobals($msg); $msg = preg_replace("/{}/", "", $msg); /* replace {element name} with form data */ $msg = preg_replace_callback("/{[^}\\s]+}/i", array($this, 'replaceWithFormData'), $msg); if (!$keepPlaceholders) { $msg = preg_replace("/{[^}\\s]+}/i", '', $msg); } return $msg; }
/** * Iterates through string to replace every * {placeholder} with row data * (added by hugh, does the same thing as parseMessageForPlaceHolder in parent * class, but for rows instead of forms) * * NOTE - I can't remember why I added this way back when in 2.x, instead of using the helper function, * I just know there was a good reason, to do with the helper func making assumptions about something * (I think to do with how form data is formatted) which weren't true when rendering list data. I have * a suspicion that in the intervening years, the helper func and the way we format data may now be * copacetic, and we could do away with this separation, and just use the normal helper func. Might be * worth testing, as this code looks like it has suffered bitrot, and doesn't do a number of things the main * helper func now does. * * @param string $msg text to parse * @param array &$row of row data * @param bool $addSlashes add slashes to the replaced data (default = false) set to true in fabrikcalc element * * @return string parsed message */ public function parseMessageForRowHolder($msg, &$row, $addSlashes = false) { $this->aRow = $row; if (!strstr($msg, '{')) { return $msg; } $this->parseAddSlases = $addSlashes; $msg = FabrikWorker::replaceWithUserData($msg); $msg = FabrikWorker::replaceWithGlobals($msg); $msg = preg_replace("/{}/", "", $msg); $this->rowIdentifierAdded = false; /* replace {element name} with form data */ /* $$$ hugh - testing changing the regex so we don't blow away PHP structures! Added the \s so * we only match non-space chars in {}'s. So unless you have some code like "if (blah) {foo;}", PHP * block level {}'s should remain unmolested. */ $msg = preg_replace_callback("/{[^}\\s]+}/i", array($this, 'replaceWithRowData'), $msg); $lang = $this->lang->getTag(); $lang = str_replace('-', '_', $lang); $msg = str_replace('{lang}', $lang, $msg); return $msg; }