You are here

views_gantt.module in Views Gantt 7.2

Same filename and directory in other branches
  1. 7 views_gantt.module

Module file for Views Gantt

File

views_gantt.module
View source
<?php

/**
 * @file
 * Module file for Views Gantt
 */

/**
 * Implements hook_views_api().
 */
function views_gantt_views_api() {
  return array(
    'api' => 3,
  );
}

/**
 * Implements hook_libraries_info().
 */
function views_gantt_libraries_info() {
  $libraries['dhtmlxgantt'] = array(
    'name' => 'dhtmlxgantt',
    'vendor url' => 'http://dhtmlx.com/docs/products/dhtmlxGantt/index.shtml',
    'download url' => 'http://dhtmlx.com/docs/products/dhtmlxGantt/index.shtml',
    'version' => '2.0',
    'files' => array(
      'js' => array(
        'codebase/dhtmlxgantt.js',
      ),
      'css' => array(
        'codebase/dhtmlxgantt.css',
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_menu().
 */
function views_gantt_menu() {
  $items = array();
  $items['views_gantt/data.json'] = array(
    'type' => MENU_CALLBACK,
    'page callback' => 'views_gantt_data_json',
    'access arguments' => array(
      'access content',
    ),
  );

  // @todo Add permission to update tasks from Gantt chart?
  $items['views_gantt/update'] = array(
    'type' => MENU_CALLBACK,
    'page callback' => 'views_gantt_update',
    'access arguments' => array(
      'access content',
    ),
  );
  $items['gantt/redirect/%/%'] = array(
    'page callback' => 'views_gantt_redirect_page',
    'page arguments' => array(
      2,
      3,
    ),
    'access callback' => 'views_gantt_redirect_access',
    'access arguments' => array(
      2,
      3,
    ),
  );
  return $items;
}
function views_gantt_data_json() {
  $data = array();
  $links = array();
  if (!isset($_SESSION['views_gantt']) && isset($_GET['view'], $_GET['display'], $_GET['project'])) {
    _views_gantt_load_view($_GET['view'], $_GET['display'], $_GET['project']);
  }
  if (isset($_SESSION['views_gantt'])) {
    $project_id = $_SESSION['views_gantt']['project']['id'];
    foreach ($_SESSION['views_gantt']['tasks'] as $id => $task) {
      if (!$task['parent_id'] && $id != $project_id) {
        $task['parent_id'] = $project_id;
      }
      $row = array(
        'id' => $id,
        'text' => $task['name'],
        'start_date' => $task['est'],
        'duration' => $task['duration'] / 8,
        'progress' => $task['percentcompleted'] / 100,
        'parent' => $task['parent_id'],
        'open' => TRUE,
        'type' => $id == $project_id ? 'project' : 'task',
      );
      if (!empty($task['columns'])) {
        foreach ($task['columns'] as $col_key => $col_val) {
          if (!array_key_exists($col_key, $row)) {
            $row[$col_key] = $col_val;
          }
        }
      }
      $data[] = $row;
      if ($task['predecessortasks']) {
        $links[] = array(
          'id' => $task['predecessortasks'] . '_' . $id,
          'source' => $task['predecessortasks'],
          'target' => $id,
          'type' => 0,
        );
      }
    }
  }
  drupal_json_output(array(
    'data' => $data,
    'links' => $links,
  ));
}
function _views_gantt_load_view($view_name, $display, $project_id) {
  $view = views_get_view($view_name);
  $exposed_filters = array();
  foreach ($_GET as $key => $value) {
    if (isset($view->exposed_data[$key])) {
      $exposed_filters[$key] = $value;
    }
  }
  if (isset($view->exposed_input)) {
    $view->exposed_input = array_merge($exposed_filters, (array) $view->exposed_input);
  }
  if (isset($view->exposed_raw_input)) {
    $view->exposed_raw_input = array_merge($exposed_filters, (array) $view->exposed_raw_input);
  }
  if (isset($view->exposed_data)) {
    $view->exposed_data = array_merge($exposed_filters, (array) $view->exposed_data);
  }
  $view
    ->preview($display, $project_id);
}

/**
 * Callback for task/project update when we change it in Gantt chart.
 */
function views_gantt_update($account = NULL) {
  $view_name = variable_get('views_gantt_view_name', NULL);

  // Show empty page in browser if request incorrect.
  if (!$view_name || empty($_POST['ids'])) {
    return NULL;
  }
  $data = $_POST;
  $id = $data['ids'];
  $style_options = views_gantt_get_style_options($view_name);
  switch ($_GET['gantt_mode']) {
    case 'tasks':
      if (isset($data[$id . '_start_date'])) {
        $fields = array(
          'date_field' => $data[$id . '_start_date'],
          'end_date_field' => $data[$id . '_end_date'],
          'progress_field' => $data[$id . '_progress'] * 100,
        );
        $node = node_load($id);
      }
      break;
    case 'links':
      if ($data[$id . '_!nativeeditor_status'] != 'deleted') {
        $source = $data[$id . '_source'];
        $target = $data[$id . '_target'];
      }
      else {
        list($source, $target) = explode('_', $id);
        $source = NULL;
      }
      $fields = array(
        'predecessor_id_field' => $source,
      );
      $node = node_load($target);
      break;
  }
  if (!empty($node) && views_gantt_update_access($node, $account)) {
    views_gantt_update_node($node, $fields, $style_options);
    $response_type = 'updated';
  }
  else {
    $response_type = 'error';
  }
  $xml = new SimpleXMLElement("<data><action type='{$response_type}' sid='{$id}' tid='{$id}' /></data>");
  drupal_add_http_header('Content-Type', 'text/xml');
  print $xml
    ->asXML();
  drupal_exit();
}

/**
 * Access callback for task/project updating.
 */
function views_gantt_update_access($node, $account = NULL) {
  if (empty($account)) {
    $account = $GLOBALS['user'];
  }

  // Check access to update node by default
  $access = node_access('update', $node, $account);

  // Allow other modules to add another access rules.
  drupal_alter('views_gantt_update_access', $access, $node, $account);
  return $access;
}

/**
 * Updates node if there are changes in it's fields.
 *
 * @param object $node
 *   Node object
 * @param array $data
 *   Array of option names (from style plugin options) => values to update
 * @param array $style_options
 *   Array of all style plugin option names
 */
function views_gantt_update_node($node, $data, $style_options) {
  $changed = FALSE;
  $lang = LANGUAGE_NONE;

  // For now we support only simple fields that
  // storing their values in array with 'value' key,
  // and datetime fields (form Date module).
  // Maybe we don't need to add another field types,
  // because we update only dates and duration.
  foreach ($data as $option_name => $option_value) {
    if (isset($style_options[$option_name])) {
      $field_name = $style_options[$option_name];
      if (isset($node->{$field_name})) {
        if (is_array($node->{$field_name})) {

          // Get field info.
          $field_info = field_info_field($field_name);

          // If field is translatable, we check if it has
          // an index equals to node language.
          $is_translatable = field_is_translatable('node', $field_info);
          if ($is_translatable && isset($node->{$field_name}[$node->language])) {
            $lang = $node->language;
          }
          $index = key($field_info['columns']);
          if ($option_name == 'end_date_field' && isset($field_info['columns']['value2'])) {
            $index = 'value2';
          }

          // If field type = datetime, modify new value before storing.
          if (isset($field_info['columns'][$index]) && $field_info['columns'][$index]['type'] == 'datetime') {
            views_gantt_normalize_date_field($option_value, 'Y-m-d H:i:s');
          }
          if ($option_value) {
            $node->{$field_name}[$lang][0][$index] = $option_value;
          }
          else {
            $node->{$field_name} = array();
          }
        }
        else {
          $node->{$field_name} = $option_value;
        }
        $changed = TRUE;
      }
    }
  }

  // If we have some changes, we need to save node.
  // Also if node saved, we remove cached gantt data from session.
  if ($changed) {
    node_save($node);
    unset($_SESSION['views_gantt']);
  }
}

/**
 * Formats date for Gantt Chart.
 */
function views_gantt_normalize_date_field(&$field, $format = 'Y-m-d H:i') {
  if (!is_numeric($field)) {
    $field = strtotime($field);
  }
  $field = format_date($field, 'custom', $format);
}

/**
 * Get style plugin options.
 */
function views_gantt_get_style_options($view_name) {
  $view = views_get_view($view_name);

  // Get style plugin options.
  // Some fields in $this->options can have incorrect names (e.g. field_date_1),
  // so we should check the real field name.
  $style_options = array();
  foreach ($view->display as $display) {
    $display_options = $display->display_options;
    if (isset($display_options['style_plugin']) && $display_options['style_plugin'] == 'gantt') {
      $style_options = $display->display_options['style_options'];
      foreach ($style_options as $key => &$value) {
        if (!empty($value) && !is_array($value) && isset($display->display_options['fields'][$value])) {
          $value = $display->display_options['fields'][$value]['field'];
        }
      }
      break;
    }
  }
  return $style_options;
}

/**
 * Implements hook_theme().
 */
function views_gantt_theme() {
  return array(
    'views_gantt_page' => array(
      'render element' => 'page',
      'template' => 'views-gantt-page',
    ),
  );
}

/**
 * Process variables for references_dialog_page.
 */
function template_process_views_gantt_page(&$variables) {

  // Generate messages last in order to capture as many as possible for the
  // current page.
  if (!isset($variables['messages'])) {
    $variables['messages'] = $variables['page']['#show_messages'] ? theme('status_messages') : '';
  }
}

/**
 * Implements hook_entity_insert().
 */
function views_gantt_entity_insert($entity, $entity_type) {
  if (views_gantt_in_dialog() && views_gantt_close_on_submit()) {
    views_gantt_close_on_redirect($entity, $entity_type);
  }
}

/**
 * Implements hook_entity_update().
 */
function views_gantt_entity_update($entity, $entity_type) {
  if (views_gantt_in_dialog() && views_gantt_close_on_submit()) {
    views_gantt_close_on_redirect($entity, $entity_type);
  }
}

/**
 * Sets our destination parameter so that the dialog will close itself after
 * redirect is completed.
 */
function views_gantt_close_on_redirect($entity, $entity_type) {
  $entity_info = entity_get_info($entity_type);
  $_GET['destination'] = 'gantt/redirect/' . $entity->{$entity_info['entity keys']['id']} . '/' . $entity_type . '?gantt-dialog-close=1&render=gantt-dialog';
}

/**
 * Implements hook_page_alter().
 */
function views_gantt_page_alter(&$page) {
  if (views_gantt_in_dialog()) {
    unset($page['page_top']);
    unset($page['page_bottom']);
    $page['#theme'] = 'views_gantt_page';
  }
}

/**
 * Check if we are in a gantt dialog.
 * @return boolean if we are in a dialog.
 */
function views_gantt_in_dialog() {
  return isset($_GET['render']) && $_GET['render'] == 'gantt-dialog';
}

/**
 * Check if we should close the dialog upon submition.
 */
function views_gantt_close_on_submit() {
  return !isset($_GET['closeonsubmit']) || $_GET['closeonsubmit'];
}
function views_gantt_redirect_access() {
  return TRUE;
}

/**
 * Page callback for our redirect page.
 */
function views_gantt_redirect_page($entity_id, $entity_type) {

  // Get some information about the entity.
  $entity = entity_load($entity_type, array(
    $entity_id,
  ));
  $entity = reset($entity);
  $entity_info = entity_get_info($entity_type);
  $entity_id = $entity->{$entity_info['entity keys']['id']};
  $entity_title = entity_label($entity_type, $entity);

  // Add appropriate javascript that will be used by the parent page to
  // fill in the required data.
  if (isset($entity_id) && views_gantt_in_dialog() && isset($_GET['gantt-dialog-close'])) {
    drupal_add_js(drupal_get_path('module', 'views_gantt') . '/js/gantt-dialog-child.js');
    drupal_add_js(array(
      'GanttDialog' => array(
        'entity_id' => $entity_id,
        'title' => $entity_title,
        'entity_type' => $entity_type,
      ),
    ), 'setting');
  }
  return '';
}

/**
 * Implements hook_form_alter().
 */
function views_gantt_form_alter(&$form, &$form_state, $form_id) {
  if (isset($_GET['render']) && $_GET['render'] == 'gantt-dialog') {

    // Prepopulate parent and project fields in add task form.
    if (!empty($_SESSION['views_gantt']['project_field'])) {
      views_gantt_prepopulate_field($_SESSION['views_gantt']['project_field'], $_GET['project'], $form, $form_state);
    }
    if (!empty($_SESSION['views_gantt']['parent_field']) && $_GET['parent'] != $_GET['project']) {
      views_gantt_prepopulate_field($_SESSION['views_gantt']['parent_field'], $_GET['parent'], $form, $form_state);
    }
  }
}

/**
 * Helper function for prepopulation fields on add form.
 */
function views_gantt_prepopulate_field($field_name, $value, &$form, $form_state) {
  if (!isset($form[$field_name])) {
    return FALSE;
  }
  if (isset($form[$field_name]['#language']) && !empty($form[$field_name]['#language'])) {
    $language = $form[$field_name]['#language'];
  }
  else {
    $language = LANGUAGE_NONE;
  }
  $widget = isset($form_state['field'][$field_name][$language]['instance']['widget']['type']) ? $form_state['field'][$field_name][$language]['instance']['widget']['type'] : '';
  if (empty($widget)) {
    return FALSE;
  }
  switch ($widget) {
    case 'options_select':
      $form[$field_name][$language]['#default_value'][0] = $value;
      break;
    case 'options_buttons':
      $form[$field_name][$language]['#default_value'] = $value;
      break;
    case 'node_reference_autocomplete':
      $form[$field_name][$language][0]['nid']['#default_value'] = $value;
      break;
    case 'entityreference_autocomplete':
      $node = node_load($value);
      $form[$field_name][$language][0]['target_id']['#default_value'] = $node->title . ' (' . $value . ')';
      break;
    case 'entityreference_autocomplete_tags':
      $node = node_load($value);
      $form[$field_name][$language]['#default_value'] = $node->title . ' (' . $value . ')';
      break;
  }
}

Functions

Namesort descending Description
template_process_views_gantt_page Process variables for references_dialog_page.
views_gantt_close_on_redirect Sets our destination parameter so that the dialog will close itself after redirect is completed.
views_gantt_close_on_submit Check if we should close the dialog upon submition.
views_gantt_data_json
views_gantt_entity_insert Implements hook_entity_insert().
views_gantt_entity_update Implements hook_entity_update().
views_gantt_form_alter Implements hook_form_alter().
views_gantt_get_style_options Get style plugin options.
views_gantt_in_dialog Check if we are in a gantt dialog.
views_gantt_libraries_info Implements hook_libraries_info().
views_gantt_menu Implements hook_menu().
views_gantt_normalize_date_field Formats date for Gantt Chart.
views_gantt_page_alter Implements hook_page_alter().
views_gantt_prepopulate_field Helper function for prepopulation fields on add form.
views_gantt_redirect_access
views_gantt_redirect_page Page callback for our redirect page.
views_gantt_theme Implements hook_theme().
views_gantt_update Callback for task/project update when we change it in Gantt chart.
views_gantt_update_access Access callback for task/project updating.
views_gantt_update_node Updates node if there are changes in it's fields.
views_gantt_views_api Implements hook_views_api().
_views_gantt_load_view