'500');
$options['height'] = array('default' => '600');
$options['type'] = array('default' => array('bluff' => 'line'));
$options['wmode'] = 'window';
$options['showlegend'] = TRUE;
$options['zoom'] = FALSE;
$options['views_charts_series_fields'] = array('default' => array());
$options['views_charts_x_labels'] = array('default' => '');
$options['engine'] = array('default' => 'bluff');
$options['y_min'] = array('default' => '0');
$options['y_max'] = array('default' => '100');
$options['y_step'] = array('default' => '25');
$options['y_legend'] = array('default' => FALSE);
$options['background_colour'] = array('default' => '#FFFFFF');
$options['series_colours'] = array('default' => '#123456,#654321,#1177ff');
return $options;
}
/**
* Options form.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
//dsm($form);
$curr_disp = $this->view->current_display;
$views_fields = $this->_get_fields();
$def_val_series = $this->options['views_charts_series_fields'];
$def_val_series = (empty($def_val_series) || !is_array($def_val_series)) ?
array(t('-- None --')) :
$def_val_series;
$form['views_charts_series_fields'] = array(
'#title' => t('Fields to be used in Chart Series'),
'#type' => 'select',
'#options' => $views_fields,
'#multiple' => TRUE,
'#required' => TRUE,
'#description' => t('These fields will be used as data fields for chart series. Fields must contain numeric data!'),
'#default_value' => $def_val_series,
);
$views_fields = $this->_get_fields();
$def_val_labels = $this->options['views_charts_x_labels'];
$def_val_labels = (empty($def_val_labels)) ?
array(t('-- None --')) :
$def_val_labels;
$form['views_charts_x_labels'] = array(
'#title' => t('Fields to be used as X axis labels'),
'#type' => 'select',
'#options' => $views_fields,
'#multiple' => FALSE,
'#required' => TRUE,
'#default_value' => $def_val_labels,
);
$form['width'] = array(
'#type' => 'textfield',
'#title' => t('Canvas width'),
'#description' => t('Canvas width, for libraries that support it you can supply a percentage, otherwise, provide a number for pixels. Defaults to 600px when input is invalid.'),
'#default_value' => $this->options['width'],
);
$form['height'] = array(
'#type' => 'textfield',
'#title' => t('Canvas height'),
'#description' => t('Canvas height in pixels only. Defaults to 500px when input is invalid.'),
'#default_value' => $this->options['height'],
);
$engines = array();
$types = array();
$api_names = array();
foreach ($apis = $this->charts_graphs_apis() as $api) {
$engines[$api->name] = $api->nice_name;
$types[$api->name] = $api->chart_types;
$api_names[] = $api->name;
}
sort($api_names);
$form['engine'] = array(
'#type' => 'select',
'#title' => t('Charting Engine'),
'#options' => $engines,
'#default_value' => $this->options['engine'],
);
$current_engine = empty($this->options['engine']) ?
$api_names[0] :
$this->options['engine'];
foreach ($types as $engine => $type) {
$default = !empty($this->options['type'][$engine]) ?
$this->options['type'][$engine] :
array_shift(array_keys($type)
);
$hidden = NULL;
if ($engine != $current_engine) {
$hidden = 'views_charts_chart_types_hidden';
}
$form['type'][$engine] = array(
'#type' => 'radios',
'#title' => t('Chart Type'),
'#options' => $type,
'#required' => TRUE,
'#default_value' => $default,
'#prefix' => sprintf(
'
',
$engine,
$hidden
),
'#suffix' => '
',
);
}
$form['y_min'] = array(
'#type' => 'textfield',
'#title' => t('Minimun value of Y Axis'),
'#default_value' => ($this->options['y_min']) ? $this->options['y_min'] : '',
);
$form['y_max'] = array(
'#type' => 'textfield',
'#title' => t('Maximun value of Y Axis'),
'#default_value' => ($this->options['y_max']) ? $this->options['y_max'] : '',
);
$form['y_step'] = array(
'#type' => 'textfield',
'#title' => t('Step value of Y Axis'),
'#default_value' => ($this->options['y_step']) ? $this->options['y_step'] : '',
);
$form['y_legend'] = array(
'#type' => 'textfield',
'#title' => t('Y Legend'),
'#default_value' => $this->options['y_legend'],
);
$form['background_colour'] = array(
'#type' => 'textfield',
'#title' => t('Graph background colour'),
'#description' => t('Define the colour in hexadecimal: #RRGGBB'),
'#default_value' => $this->options['background_colour'],
);
$form['series_colours'] = array(
'#type' => 'textfield',
'#title' => t('Series colours'),
'#description' => t('Define the colour of each series as a comma separated list of hexadecimal colour definitions. Ex: #123456,#654321,#1177ff'),
'#default_value' => $this->options['series_colours'],
);
$form['showlegend'] = array(
'#type' => 'checkbox',
'#title' => t('Show Legend'),
'#description' => t('Show or hide the legend, if the library supports it.'),
'#default_value' => $this->options['showlegend'],
);
$form['zoom'] = array(
'#type' => 'checkbox',
'#title' => t('Zoom'),
'#description' => t('Flot only! Adds a second smaller version of the graps that allows you to zoom in and out.'),
'#default_value' => $this->options['zoom'],
);
$form['display_in_single_chart'] = array(
'#type' => 'checkbox',
'#title' => t('Display in single chart'),
'#description' => t('Try to display multiseries in single chart.'),
'#default_value' => $this->options['display_in_single_chart'],
);
$form['missing_default_value'] = array(
'#type' => 'textfield',
'#title' => t('Missing default value'),
'#description' => t('Enter default value for empty cell for series array merge. Let blank for use "null"'),
'#default_value' => $this->options['missing_default_value'],
'#dependency' => array(
'edit-style-options-display-in-single-chart' => array(TRUE),
),
);
$form['multiseries_sort_type'] = array(
'#type' => 'select',
'#title' => t('Sort method for x label'),
'#default_value' => array($this->options['multiseries_sort_type']),
'#options'=>array('string'=>'SORT_STRING (php)', 'numeric'=>'SORT_NUMERIC (php)', 'regular'=>'SORT_REGULAR (php)', 'natural'=>'SORT_NATURAL (php)', 'date'=>'Sort on date with strtotime()'),
'#description' => t('Enter the sort method for x_label'),
'#dependency' => array(
'edit-style-options-display-in-single-chart' => array(TRUE),
),
);
$form['wmode'] = array(
'#type' => 'select',
'#title' => t('Window Mode (for flash charts only)'),
'#options' => $this->charts_graphs_get_wmode_values(),
'#default_value' => $this->options['wmode'],
);
}
/**
* Render the display in this style.
*/
function render() {
// Group the rows according to the grouping field, if specified.
$sets = $this->render_grouping($this->view->result, $this->options['grouping']);
$output = '';
if($this->options['display_in_single_chart']){
$output .= $this->_render_multiseries($sets, 'Charts');
}
else{
foreach ($sets as $title => $rows) {
$output .= $this->_render($rows, $title);
}
}
unset($this->view->row_index);
return $output;
}
function _render_multiseries($sets, $title){
if (!isset($this->options['engine'])) {
drupal_set_message("No engine specified for display '" . $this->view->current_display . "' on view '" . $this->view->name . "'");
return FALSE;
}
if ($canvas = $this->charts_graphs_get_graph($this->options['engine'])) {
//Add nd@oieau.fr => multiseries
$series = array();
foreach ($sets as $title => $rows) {
$cgp_data = $this->_transform_data($rows);
$series['Values'][$title] = $cgp_data->rows['Value'];
$series['x_labels'][$title] = $cgp_data->x_labels;
}
//Check if all series have the same length ans same label, if not try to merge series with null values or generate error ?
$series = $this->process_series($series);
$canvas->set_data($series['Values'], $series['x_labels']);
$canvas->x_type = $cgp_data->x_type;
//end addmultiseries
$curr_disp = $this->view->current_display;
$canvas->title = $this->view->display[$curr_disp]->handler->get_option('title');
$canvas->type = $this->options['type'][$this->options['engine']];
$canvas->y_legend = $this->options['y_legend'];
if (isset($this->options['y_min']) && !empty($this->options['y_min'])) {
$canvas->y_min = $this->options['y_min'];
}
if (isset($this->options['y_max']) && !empty($this->options['y_max'])) {
$canvas->y_max = $this->options['y_max'];
}
if (isset($this->options['y_step']) && !empty($this->options['y_step'])) {
$canvas->y_step = $this->options['y_step'];
}
if (isset($this->options['background_colour'])) {
$background_colour = trim($this->options['background_colour']);
if (!empty($background_colour)) {
$canvas->colour = $background_colour;
}
}
if (isset($this->options['series_colours'])) {
$series_colours = explode(',', $this->options['series_colours']);
if (count($series_colours)) {
$canvas->series_colours = $series_colours;
}
}
// make the user input a bit more resilient;
$width = str_replace("px", "", $this->options['width']); // just for people who are confused and still added this
$width = ((is_numeric($width)) || (is_numeric(str_replace("%", "", $width)))) ? $width : "600";
$height = str_replace("px", "", $this->options['height']);
$height = (is_numeric($height)) ? $height : "500";
$canvas->width = $width;
$canvas->height = $height;
$canvas->wmode = $this->options['wmode'];
$canvas->showlegend = $this->options['showlegend'];
$canvas->zoom = $this->options['zoom']; // Flot only
//'admin/structure/views' - issues with javascript etc. Do not try to render
if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views') {
$msg = t("Preview not available for Charts display style. Please view on a full page");
$msg = "$msg
";
return $msg;
}
views_charts_invoke_all('views_charts_graph_alter', array(&$canvas));
$element = $canvas->get_chart();
return render($element);
}
else {
drupal_set_message("Graph with engine " . $this->options['engine'] . " not available");
}
}
function _render($rows, $title) {
if (!isset($this->options['engine'])) {
drupal_set_message("No engine specified for display '" . $this->view->current_display . "' on view '" . $this->view->name . "'");
return FALSE;
}
if ($canvas = $this->charts_graphs_get_graph($this->options['engine'])) {
$cgp_data = $this->_transform_data($rows);
$canvas->set_data($cgp_data->rows, $cgp_data->x_labels);
$canvas->x_type = $cgp_data->x_type;
$curr_disp = $this->view->current_display;
$canvas->title = $this->view->display[$curr_disp]->handler->get_option('title');
$canvas->type = $this->options['type'][$this->options['engine']];
$canvas->y_legend = $this->options['y_legend'];
if (isset($this->options['y_min']) && !empty($this->options['y_min'])) {
$canvas->y_min = $this->options['y_min'];
}
if (isset($this->options['y_max']) && !empty($this->options['y_max'])) {
$canvas->y_max = $this->options['y_max'];
}
if (isset($this->options['y_step']) && !empty($this->options['y_step'])) {
$canvas->y_step = $this->options['y_step'];
}
if (isset($this->options['background_colour'])) {
$background_colour = trim($this->options['background_colour']);
if (!empty($background_colour)) {
$canvas->colour = $background_colour;
}
}
if (isset($this->options['series_colours'])) {
$series_colours = explode(',', $this->options['series_colours']);
if (count($series_colours)) {
$canvas->series_colours = $series_colours;
}
}
// make the user input a bit more resilient;
$width = str_replace("px", "", $this->options['width']); // just for people who are confused and still added this
$width = ((is_numeric($width)) || (is_numeric(str_replace("%", "", $width)))) ? $width : "600";
$height = str_replace("px", "", $this->options['height']);
$height = (is_numeric($height)) ? $height : "500";
$canvas->width = $width;
$canvas->height = $height;
$canvas->wmode = $this->options['wmode'];
$canvas->showlegend = $this->options['showlegend'];
$canvas->zoom = $this->options['zoom']; // Flot only
//'admin/structure/views' - issues with javascript etc. Do not try to render
if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views') {
$msg = t("Preview not available for Charts display style. Please view on a full page");
$msg = "$msg
";
return $msg;
}
views_charts_invoke_all('views_charts_graph_alter', array(&$canvas));
$element = $canvas->get_chart();
return render($element);
}
else {
drupal_set_message("Graph with engine " . $this->options['engine'] . " not available");
}
}
//Transform data series for have same column number and replace missing value by the null or 0 value
function process_series($series){
$titles = array();
$values =array();
//First, we need to reoganise all x_label
//notes :
// - we probably need to ask to user the sort method => date, numeric, text
// - we need to ask the default value for missing columns values
foreach($series['x_labels'] as $serie_x_labels=>$serie_titles){
foreach($serie_titles as $t){
if($t!='' && !in_array($t, $titles))
$titles[] = $t;
}
//nd@oieau.fr : 21/01/2013 : for date sorting we can have some strange sort due to date format and series on different month
//array('string'=>'SORT_STRING (php)', 'numeric'=>'SORT_NUMERIC (php)', 'regular'=>'SORT_REGULAR (php)', 'natural'=>'SORT_NATURAL (php)', 'date'=>'Sort on date with strtotime()'
switch($this->options['multiseries_sort_type']){
case 'string':
sort($titles, SORT_STRING);
break;
case 'numeric':
sort($titles, SORT_NUMERIC);
break;
case 'regular':
sort($titles, SORT_REGULAR);
break;
case 'natural':
sort($titles, SORT_NATURAL);
break;
case 'date':
//In this case we need to convert all value in date value with the strtotime function
$dates = array();
foreach($titles as $k=>$v){
$dates[$k] =strtotime($v);
}
array_multisort($dates, $titles);
sort($titles, SORT_STRING);
break;
default:
sort($titles); //SORT_REGULAR or SORT_STRING or SORT_NUMERIC or pre-process with strtotime()
}
}
//We also need to reoganize all data values for have right index and give default values
if($this->options['missing_default_value']!=''){
$default_value = $this->options['missing_default_value'];
}
else{$default_value =null;}
foreach($series['Values'] as $serie_id=>$serie_data){
foreach($titles as $index => $serie_x_label){
//If we find a value for the $serie_x_label
$search_i = array_search($serie_x_label, $series['x_labels'][$serie_id]);
if($search_i!==false){
$values[$serie_id][$index] = $series['Values'][$serie_id][$search_i];
}
else{
$values[$serie_id][$index] = $default_value;
}
}
}
$format_series['Values']=$values;
$format_series['x_labels']=$titles;
return $format_series;
}
/**
* Transform data from Views-centric representation into standard Charts and
* Graphs input format.
*
* @return
*/
function _transform_data($db_rows) {
$series_column_names = $this->options['views_charts_series_fields'];
$x_label_column = $this->options['views_charts_x_labels'];
$x_isnum = FALSE;
$x_isdate = FALSE;
$views_fields = $this->view->field;
$fields_x_names = array();
$series_full_names = $this->_get_fields(TRUE);
$x_label_alias_found = FALSE;
$aliases = array();
foreach ($views_fields as $idx => $f) {
$db_alias = $idx;//$f->real_field;
if (in_array($idx, $this->options['views_charts_series_fields'])) {
$fields_x_names[$idx] = $series_full_names[$idx];
}
$aliases[$idx] = $idx;
if ((trim($x_label_column) == trim($idx)) && ($x_label_alias_found == FALSE)) {
$x_label_column = $idx;
$x_isdate = (((get_class($views_fields[$idx]) == 'views_handler_field_date') &&
($views_fields[$idx]->options['date_format'] == 'custom') &&
($views_fields[$idx]->options['custom_date_format'] == 'U')) ||
((get_class($views_fields[$idx]) == 'views_handler_field_field') &&
($views_fields[$idx]->field_info['type'] == 'date') &&
(date_format_type_format($views_fields[$idx]->options['settings']['format_type']) == 'U')));
$x_isnum = ((isset($views_fields[$idx]->field_info)) && ($views_fields[$idx]->field_info['module'] == 'number'));
$x_label_alias_found = TRUE;
}
}
// We need to re-map the db results array so that labels are indexes.
$rows = array();
$labels = array();
foreach ($db_rows as $rowid => $row) {
$cols = (array) $row;
if (isset($x_label_column)) {
if (!$x_isnum) {
//Fix nd@oieau.fr
// This works for all Drupal core fields
$class_name = get_class($views_fields[$aliases[$x_label_column]]);
if ($class_name == 'views_handler_field_date' ||
(isset($this->view->result[$rowid]->_field_data[$x_label_column]['entity'])) ||
(get_class($views_fields[$aliases[$x_label_column]]) == 'views_handler_field_node') ||
(get_class($views_fields[$aliases[$x_label_column]]) == 'views_handler_field')
) {
$labels[] = $views_fields[$aliases[$x_label_column]]->render($this->view->result[$rowid]);
}
// This is for date fields
//Fix nd@oieau.fr => http://drupal.org/node/1758890 &&
elseif (
in_array($class_name , array( 'views_handler_field_field', 'flot_views_handler_field_field'))
&& in_array($views_fields[$aliases[$x_label_column]]->field_info['type'], array('date', 'datestamp', 'datetime'))
) {
$fieldname = "field_" .$x_label_column;
$field = $this->view->result[$rowid]->$fieldname;
$labels[] = strip_tags($field[0]['rendered']['#markup']);
}
elseif ((get_class($views_fields[$aliases[$x_label_column]])== 'flot_views_handler_field_taxonomy')) {
$labels[] = $views_fields[$aliases[$x_label_column]]->render($this->view->result[$rowid]);
}
// This is when we have no clue
else {
$labels[] = t('unknown');
}
}
else {
$labels[] = $views_fields[$aliases[$x_label_column]]->original_value; // no rendering for numbers
}
foreach ($fields_x_names as $key => $label) {
$rows[$label][] = (float) $this->view->field[$key]->theme($row);
}
}
else {
$labels[] = t('unknown');
}
}
$ret = new stdClass();
$ret->rows = $rows;
$ret->x_labels = $labels;
if ($x_isdate) {
$ret->x_type = 'date';
}
elseif ($x_isnum) {
$ret->x_type = 'number';
}
else {
$ret->x_type = 'text';
}
return $ret;
}
/**
*
* @param $return_pretty_name Return only the label, so we can use it
* on chart.
* @return
*/
function _get_fields($return_pretty_name = FALSE) {
$handlers = $this->display->handler->get_handlers('field');
$avail_fields = array();
if (is_array($handlers)) {
foreach ($handlers as $field => $handler) {
$field_alias = $handler->options['table'] . '_' . $handler->options['field'];
$relationship = '';
$all_relationships = $this->_get_relationships();
$rel = $handler->options['relationship'];
$rel = isset($all_relationships[$rel]) ?
$all_relationships[$rel] :
NULL;
if (!empty($rel)) {
// $field_name = $rel->fieldprefix . '.' . $val->options['field']; // reserved, not used
// $field_alias = $rel->fieldprefix . '_' . $val->options['field'];
$relationship = (empty($rel)) ? '' : '[' . $rel->label . '] ';
}
$label = ($handler->label()) ? $handler->label() : $handler->ui_name();
$field_name = $relationship . $handler->definition['group'] . ': ' .
$handler->definition['title'] . ' (' . $label . ')';
$avail_fields[$field] = $return_pretty_name ? $label : $field_name;
}
}
return $avail_fields;
}
function _get_relationships() {
$default_rels = array();
$curr_disp_rels = array();
$curr_disp = $this->view->current_display;
$default_rels = $this->view->display['default']->handler->options['relationships'];
$curr_displ_rels = isset($this->view->display[$curr_disp]->handler->options['relationships']) ?
$this->view->display[$curr_disp]->handler->options['relationships'] :
NULL;
$default_rels = (is_array($default_rels)) ? $default_rels : array();
$curr_displ_rels = (is_array($curr_displ_rels)) ? $curr_displ_rels : array();
$relationships = array_merge($default_rels, $curr_displ_rels);
$all_rels = array();
$base_table = $this->view->base_table;
if (is_array($relationships)) {
foreach ($relationships as $key => $val) {
$obj = new stdClass();
$obj->fieldprefix = $base_table . '_' . $val['table'];
$obj->label = $val['label'];
$obj->table = $val['table'];
$obj->field = $val['field'];
$all_rels[$key] = $obj;
}
}
return $all_rels;
}
function charts_graphs_apis($library = NULL) {
$function = function_exists('chart_graphs_apis') ?
'chart_graphs_apis' :
'charts_graphs_apis';
if ($library === NULL) {
return call_user_func($function);
}
else {
return call_user_func($function, $library);
}
}
function charts_graphs_get_graph($library) {
$function = function_exists('chart_graphs_get_graph') ?
'chart_graphs_get_graph' :
'charts_graphs_get_graph';
if (function_exists($function)) {
return call_user_func($function, $library);
}
else {
return FALSE;
}
}
function charts_graphs_get_wmode_values() {
if (class_exists('ChartCanvas')) {
$class_name = 'ChartCanvas';
}
else {
$class_name = 'ChartsGraphsFlashCanvas';
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'charts_graphs') .
'/charts_graphs_flash_canvas.class.inc';
}
return call_user_func(array($class_name, 'wmode_values'));
}
}