'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')); } }