filename;
}
/*
* -------------------------------------------------------------------------
* HOOKS IMPLEMENTATIONS
* -------------------------------------------------------------------------
*/
/**
* Implements hook_help().
*
* Displays help and module information.
*/
function webform_chart_help($section, $arg) {
switch ($section) {
case "admin/help#webform_chart":
return '
' . t("Provides graphical rendering of Webform results as charts") . '
';
}
}
/**
* Implements hook_permission().
*
* Two permissions available:
* - Configure Webform Charts : allow given roles to configure the charting
* and backend
* to use on webform settings pages.
* - View Webform Charts : allow given roles to access the chart
* results page.
*/
function webform_chart_permission() {
return array(
'configure_webform_charts' => array(
'title' => t('Configure Webform Charts'),
'description' => t('Allows users to configure back ends for webform charts.'),
),
'view_webform_charts' => array(
'title' => t('View Webform Charts'),
'description' => t('Allows users to visit the chart display result page.'),
),
);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add setting to webform configure: the webform settings page.
* This function add settings to choose which backend this particular webform
* will use.
*/
function webform_chart_form_webform_configure_form_alter(&$form, &$form_state) {
// Check if user has admin permission on Webform Charts.
if (user_access('configure_webform_charts')) {
// Add configuration to the setting form.
// This helper method is defined in the webform_chart.admin.inc file.
_webform_chart_form_configure($form, $form_state);
// Add a new validate callback function (used to validate charting config).
$form['#validate'][] = '_webform_chart_form_configure_validate';
// Add a new submit callback function, used to save charting config.
$form['#submit'][] = '_webform_chart_form_configure_save';
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add setting to webform all components: configure charting.
*/
function webform_chart_form_webform_component_edit_form_alter(&$form, $form_state) {
// Check if charting is applicable.
if (!_webform_chart_is_applicable($form['type']['#value'])) {
return;
}
// Retrieve the existing value for the webform charting components.
$charting = array('webform_chart_backend' => NULL);
$args = $form_state['build_info']['args'][1];
if (isset($args['charting'])) {
$charting = unserialize($args['charting']);
}
// Check if user has admin permission on Webform Charts.
if (user_access('configure_webform_charts')) {
// Retrieve the backend to use.
$nid = $form['nid']['#value'];
$charting_config = unserialize(_webform_chart_retrieve_charting_config($nid));
$backend = $charting_config['backend'];
if ($charting_config['config_method'] == 1) {
$fn = $backend . '_wfc_backend_configure';
$form['charting'] = array(
'#type' => 'fieldset',
'#title' => t('Charting Configuration'),
'#description' => t('Configure the options for your charting backend.'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
if ($backend) {
$form['charting']['activate'] = array(
'#type' => 'checkbox',
'#title' => t('Render this component as a chart.'),
'#default_value' => isset($charting['activate']) ? $charting['activate'] : FALSE,
'#ajax' => array(
'callback' => 'webform_chart_form_webform_component_ajax_callback',
'wrapper' => 'webform_chart_form_webform_component_ajax_wrapper',
),
);
if (function_exists($fn)) {
$form['charting']['config'] = array(
'#type' => 'container',
'#prefix' => '',
'#suffix' => '
',
);
if ((isset($charting['activate']) && $charting['activate']) || ((isset($form_state['values']['charting']['activate'])) && $form_state['values']['charting']['activate'])) {
$backend_config = $fn($charting['config']);
$form['charting']['config'] = array_merge($form['charting']['config'], $backend_config);
}
}
else {
$form['charting']['markup'] = array(
'#markup' => '' . t('Charts has been disabled for this webform.') . '',
);
}
}
}
}
}
/**
* Implements hook_webform_component_presave().
*
* Validate webform chart settings before saving.
*/
function webform_chart_webform_component_presave(&$component) {
if (!_webform_chart_is_applicable($component['type'])) {
return;
}
$charting_config = unserialize(_webform_chart_retrieve_charting_config($component['nid']));
// Only if "per component" charting.
if ($charting_config['config_method'] == 1) {
// Retrieve the backend to use.
$backend = $charting_config['backend'];
// Call the backend charting validate function.
$fn = $backend . '_wfc_component_validate';
if (function_exists($fn) && $component['charting']['activate'] == 1) {
// Add config to component values to ensure symetry on the
// component_validation hook for both component and form configuration.
$component['values'] = $component['charting']['config'];
$fn($component);
$component['charting']['config'] = $fn($component);
}
elseif (!is_array($component['charting'])) {
$component['charting'] = unserialize($component['charting']);
}
}
}
/**
* Implements hook_webform_component_insert().
*
* Save webform chart settings.
*/
function webform_chart_webform_component_insert($component) {
webform_chart_webform_component_update($component);
}
/**
* Implements hook_webform_component_update().
*
* Update webform chart settings.
*/
function webform_chart_webform_component_update($component) {
if (!_webform_chart_is_applicable($component['type'])) {
return;
}
if (isset($component['charting'])) {
db_merge('webform_component')
->key(array('nid' => $component['nid'], 'cid' => $component['cid']))
->fields(array(
'charting' => serialize($component['charting']),
))
->execute();
}
}
/**
* Implements hook_menu().
*
* Adds the charting result page on each Webform node.
*/
function webform_chart_menu() {
$items['node/%webform_menu/chart-results'] = array(
'title' => 'Results as charts',
'page callback' => 'webform_chart_page',
'page arguments' => array(1),
'access arguments' => array('view_webform_charts'),
'weight' => 7,
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* Implements hook_theme().
*/
function webform_chart_theme() {
$theme = array(
// Theme the result page.
'webform_chart_page' => array(
'variables' => array(
'node' => NULL,
'data' => NULL,
'sids' => array(),
'component' => NULL,
),
'template' => 'templates/webform-chart-page',
),
'webform_chart_items' => array(
'variables' => array(
'raw_items' => NULL,
'configs' => NULL,
),
'template' => 'templates/webform-chart-items',
),
'webform_chart_item' => array(
'variables' => array(
'raw_item' => NULL,
'configs' => NULL,
),
'template' => 'templates/webform-chart-item',
),
// Theme the help on setting page.
'webform_chart_form_help' => array(
'file' => 'webform_chart.admin.inc',
'variables' => array(),
),
);
return $theme;
}
/**
* Implements hook_init().
*
* This is a trick to be able to use AJAX in webform component edit page.
* The trick has been found here:
* https://drupal.org/node/1091354#comment-5323294
* If someone find better, please fill free to help
*/
function webform_chart_init() {
// Hack to fix webforms.
if ($_GET['q'] == 'system/ajax' && isset($_POST['form_id'])) {
foreach (webform_components() as $component_type => $component_info) {
webform_component_include($component_type);
}
}
}
/*
* -----------------------------------------------------------------------
* TEMPLATE PREPROCESS IMPLEMENTATIONS
* -----------------------------------------------------------------------
*/
/**
* Template pre-process function.
*
* Preprocess the chart page to build all raw data necessary for rendering
* using the template file: webform-chart-page.tpl.php.
*/
function template_preprocess_webform_chart_page(&$variables) {
// Add the css.
drupal_add_css(drupal_get_path('module', 'webform_chart') . '/css/webform-chart.css');
// Get back the backend to use for rendering.
$webform = $variables['node']->webform;
$configs = isset($webform['charting']) ? unserialize($webform['charting']) : NULL;
// If charting is not defined: it's useless to continue.
if ($configs == NULL || $configs['backend'] == 'none') {
// Render the results if the charting backend exists.
drupal_set_message(t('Charts has been disabled for this webform.'), 'warning');
return;
}
// The component data, contains component_data and row_data.
$data = $variables['data'];
// Add a warning if there is no elements in the webform.
if (count($data) == 0) {
drupal_set_message(t('There are no elements in this webform.'), 'warning');
return;
}
// Create page title.
$variables['page_title'] = t('Chart results');
// Render items.
$variables['charting'] = theme('webform_chart_items', array('raw_items' => $data, 'configs' => $configs));
}
/**
* Template pre-process function.
*
* Preprocess the chart items list to build all raw data necessary for
* rendering using the template file: webform-chart-items.tpl.php.
*/
function template_preprocess_webform_chart_items(&$variables) {
$items = array();
$configs = $variables['configs'];
// Backend can be null when components are improperly setted.
// Example: when the module has just been activated but not yet configured.
if ($configs['backend']) {
// Build all rendered items (using template_preprocess_*...*_item).
foreach ($variables['raw_items'] as $item) {
// If charting data exist, get the config.
if ($item['component_data']['charting']) {
$config = unserialize($item['component_data']['charting']);
}
// Do not render an item that does not need charting.
if ($configs['config_method'] == 1 && (!$item['component_data']['charting'] || $config == NULL || !array_key_exists('activate', $config) || !$config['activate'])) {
continue;
}
// Do not render unsupported elements.
if (!_webform_chart_is_applicable($item['component_data']['type'])) {
continue;
}
$items[] = theme('webform_chart_item', array('raw_item' => $item, 'configs' => $variables['configs']));
}
}
// If configuration is not correct or if no items have been individually
// setted: display a status info.
if (!$configs['backend'] || ($configs['config_method'] == 1 && count($items) == 0)) {
drupal_set_message(t('None of the components are activated for chart rendering.'), 'status');
}
// Return the rendered items.
$variables['items'] = $items;
return $items;
}
/**
* Template pre-process function.
*
* Preprocess a chart item to build all raw data necessary for rendering using
* the template file: webform-chart-item.tpl.php.
*/
function template_preprocess_webform_chart_item(&$variables) {
$data = $variables['raw_item'];
$configs = $variables['configs'];
$cid = $variables['id'];
$number_responses = 0;
$row_data = $data['row_data'];
$component_data = $data['component_data'];
$config = array();
// Build some (maybe!) usefull variables for templating.
$variables['base_path'] = base_path();
// Populate the page template suggestions.
$suggestions = theme_get_suggestions(arg(), 'webform_chart_item');
if ($suggestions) {
$variables['theme_hook_suggestions'] = $suggestions;
}
// NOTE: just in case some elements don't have row_data but it should not.
if (is_array($row_data)) {
// Retrieve the component name, generally the question
// this will be used as the chart title.
$name = $component_data['name'];
// Retrieve the description (if applicable).
$description = isset($component_data['extra']['description']) ? $component_data['extra']['description'] : '';
if ($configs['config_method'] == 0) {
$config = $configs;
}
else {
// Retrieve the component charting configuration.
$config = unserialize($component_data['charting']);
}
// Add the chart to the chart list.
$item = array(
'#chart_id' => "chart_" . $cid,
'#title' => $name,
'#description' => $description,
'#component' => $component_data,
'#config' => $config,
);
// Each grid question is render as an item : thus change the $data format.
$datas = isset($row_data['table_rows']) ? $row_data['table_rows'] : $row_data;
// Assign #data and #labels to the chart.
foreach ($datas as $value) {
// @todo Fix this, this is dumb.
// Perhaps add the count of responses to the query ?
//if ($value[0] == 'Average submission length in words (ex blanks)') {
$number_responses += $value[1];
//}
$item['#data'][] = intval($value[1]);
$item['#labels'][] = $value[0];
}
$item['#total_responses'] = $number_responses;
}
// Return the rendered item.
$variables['item'] = $item;
if ($number_responses == 0) {
$variables['rendered_item'] = '' . t('There are no results for this component.') . '
';
return $variables['rendered_item'];
}
// Call the backend item render using hook:
// #backendname#_wfc_component_render($item).
$fn = $configs['backend'] . '_wfc_component_render';
if (function_exists($fn)) {
$variables['rendered_item'] = $fn($item);
}
else {
drupal_set_message(t('The selected rendering backend has no rendering function: %fn', array('%fn' => $fn)), 'error');
return;
}
return $variables['rendered_item'];
}
/*
* ----------------------------------------------------------------------
* HELPER FUNCTIONS
* ----------------------------------------------------------------------
*/
/**
* Retrieves the backend bundler module name used for chart rendering.
*
* @param Integer $nid
* The webform nid.
*/
function _webform_chart_retrieve_charting_config($nid) {
// Retrieve the backend to use.
$result = db_select('webform', 'wf')
->fields('wf', array('charting'))
->condition('nid', $nid)
->execute()
->fetchAssoc();
return $result['charting'];
}
/**
* Check if charting is applicable for the input component type.
*
* @input $type the component type
*
* @return bool
* TRUE/FALSE: If the charting is application for this component type.
*/
function _webform_chart_is_applicable($type) {
if ($type == 'fieldset') {
return FALSE;
}
elseif ($type == 'pagebreak') {
return FALSE;
}
elseif ($type == 'markup') {
return FALSE;
}
elseif ($type == 'draggable_list') {
return FALSE;
}
else {
return TRUE;
}
}
/**
* Ajax callback implementation.
*
* Used in each component edit form to render the backend config.
*/
function webform_chart_form_webform_component_ajax_callback($form, $form_state) {
return $form['charting']['config'];
}