View source  
  <?php
define('SLICKGRID_CALLBACK_PATH', 'slickgrid/callback');
function slickgrid_init() {
  global $conf;
  if (module_exists('lightbox2')) {
    
    if (arg(0) == 'slickgrid' && arg(1) == 'callback') {
      $conf['lightbox2_lite'] = TRUE;
    }
    else {
      $menu_item = menu_get_item();
      
      if ($menu_item['page_callback'] == 'views_page') {
        $view = views_get_view($menu_item['page_arguments'][0]);
        if ($view->display['default']->display_options['style_plugin'] == 'slickgrid') {
          
          $conf['lightbox2_lite'] = TRUE;
        }
      }
    }
  }
}
function slickgrid_menu() {
  
  $items[SLICKGRID_CALLBACK_PATH . '/%'] = array(
    'page callback' => 'slickgrid_callback',
    'page arguments' => array(
      2,
    ),
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'slickgrid.callbacks.inc',
  );
  return $items;
}
function slickgrid_menu_alter(&$items) {
  
  $items['content/js_add_more']['page callback'] = 'slickgrid_content_add_more_js';
  $items['content/js_add_more']['file'] = 'slickgrid.ahah.inc';
  $items['content/js_add_more']['file path'] = drupal_get_path('module', 'slickgrid');
  
  $items['filefield/ahah/%/%/%']['page callback'] = 'slickgrid_filefield_js';
  $items['filefield/ahah/%/%/%']['file'] = 'slickgrid.ahah.inc';
  $items['filefield/ahah/%/%/%']['file path'] = drupal_get_path('module', 'slickgrid');
  
  $items['nodereference/autocomplete']['page callback'] = 'slickgrid_nodereference_autocomplete';
  $items['nodereference/autocomplete']['file'] = 'slickgrid.ahah.inc';
  $items['nodereference/autocomplete']['file path'] = drupal_get_path('module', 'slickgrid');
}
function slickgrid_views_api() {
  return array(
    'api' => 2,
  );
}
function slickgrid_views_pre_view(&$view, $display_id) {
  global $conf;
  
  if ($view->display_handler
    ->get_option('style_plugin') == 'slickgrid') {
    
    $conf['preprocess_js'] = 0;
    $style_options = $view->display_handler
      ->get_option('style_options');
    if ($view->display_handler
      ->get_option('use_pager')) {
      
      $view->display_handler
        ->set_option('use_pager', FALSE);
      
      $style_options['pager'] = TRUE;
    }
    $view->display_handler
      ->set_option('style_options', $style_options);
    
    if ($style_options['collapsible_taxonomy_field']) {
      
      $parent_relationship = array(
        'label' => t('Slickgrid parent'),
        'required' => 0,
        'id' => 'parent',
        'table' => 'term_hierarchy',
        'field' => 'parent',
      );
      $view
        ->set_item($display_id, 'relationship', 'slickgrid_parent_relationship', $parent_relationship);
      
      $parent_tid_field = array(
        'exclude' => 1,
        'id' => 'slickgrid_parent_tid',
        'table' => 'term_data',
        'field' => 'tid',
        'relationship' => 'slickgrid_parent_relationship',
      );
      $view
        ->set_item($display_id, 'field', 'slickgrid_parent_tid', $parent_tid_field);
      
      $tid_field = array(
        'exclude' => 1,
        'id' => 'slickgrid_tid',
        'table' => 'term_data',
        'field' => 'tid',
      );
      $view
        ->set_item($display_id, 'field', 'slickgrid_tid', $tid_field);
      
      $existing_sorts = $view
        ->get_items('sort');
      
      foreach (array_keys($existing_sorts) as $existing_sort_id) {
        $view
          ->set_item($display_id, 'sort', $existing_sort_id, null);
      }
      
      foreach (array(
        'slickgrid_parent_relationship',
        'none',
      ) as $relationship) {
        $id = 'slickgrid_sort_' . $relationship;
        $sort = array(
          'order' => 'ASC',
          'id' => $relationship,
          'table' => 'term_data',
          'field' => 'weight',
          'relationship' => $relationship,
        );
        $view
          ->set_item($display_id, 'sort', $id, $sort);
      }
      
      foreach ($existing_sorts as $existing_sort_id => $existing_sort) {
        $view
          ->set_item($display_id, 'sort', $existing_sort_id, $existing_sort);
      }
    }
  }
}
function slickgrid_theme() {
  return array(
    
    'slickgrid' => array(
      'arguments' => array(
        'options' => array(),
        'columns' => array(),
        'data' => array(),
      ),
    ),
    'slickgrid_views_plugin_table' => array(
      'arguments' => array(
        'form' => array(),
      ),
    ),
  );
}
function slickgrid_slickgrid_plugins() {
  $path = drupal_get_path('module', 'slickgrid');
  $plugins = array(
    'TextCellEditor' => array(
      'description' => t('Text cell'),
      'js' => $path . '/js/slickgrid.editors.js',
      'css' => $path . '/css/slickgrid.editors.css',
      'field_type' => array(
        'node_title',
        'date_text',
      ),
      
      'plugin_type' => 'editor',
    ),
    'LongTextCellEditor' => array(
      'description' => t('Simple textarea'),
      'js' => $path . '/js/slickgrid.editors.js',
      'css' => $path . '/css/slickgrid.editors.css',
      'field_type' => array(
        'text_textarea',
        'node_revisions_body',
      ),
      'plugin_type' => 'editor',
    ),
    'nodeFormEditor' => array(
      'description' => t('Popup node form'),
      'js' => $path . '/js/slickgrid.editors.js',
      'css' => $path . '/css/slickgrid.editors.css',
      'plugin_type' => 'editor',
    ),
    
    'requiredFieldValidator' => array(
      'description' => t('Required field'),
      'js' => $path . '/js/slickgrid.validators.js',
      'plugin_type' => 'validator',
    ),
    
    'textFilter' => array(
      'description' => t('Text filter'),
      'js' => $path . '/js/slickgrid.filters.js',
      'plugin_type' => 'filter',
    ),
  );
  return $plugins;
}
function slickgrid_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'delete':
      
      slickgrid_delete_settings(array(
        'uid' => $account->uid,
      ));
      break;
  }
}
function slickgrid_form_alter(&$form, $form_state, $form_id) {
  global $user;
  switch ($form_id) {
    case 'views_ui_rearrange_form':
      
      $form['buttons']['submit']['#submit'][] = 'slickgrid_views_ui_rearrange_form_submit';
      break;
  }
  
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id && arg(0) == 'slickgrid') {
    $form['#pre_render'][] = 'slickgrid_node_form_pre_render';
  }
}
function slickgrid_preprocess_views_view_slickgrid(&$vars) {
  global $user;
  
  module_load_include('inc', 'node', 'node.pages');
  $view = $vars['view'];
  
  $view->style_plugin->options['settings'] = (array) slickgrid_get_settings($user->uid, $view->name) + (array) $view->style_plugin->options['settings'];
  
  $view->style_plugin->options['headerHeight'] = 42;
  
  $view->style_plugin->options['enableCellNavigation'] = $view->style_plugin->options['editable'];
  $view->style_plugin->options['enableAddRow'] = false;
  
  if (is_numeric($view->style_plugin->options['settings']['viewport_height'])) {
    $view->style_plugin->options['viewport_height'] = $view->style_plugin->options['settings']['viewport_height'];
  }
  
  if (isset($view->style_plugin->options['settings']['forceFitColumns'])) {
    $view->style_plugin->options['forceFitColumns'] = (int) $view->style_plugin->options['settings']['forceFitColumns'];
  }
  $result = $vars['rows'];
  $handler = $view->style_plugin;
  $handlers = $view->style_plugin->display->handler
    ->get_handlers('field');
  $fields =& $view->field;
  $ordered_columns = array();
  $unordered_columns = array();
  
  foreach ($handlers as $field => $handler) {
    
    if ($handler->options['exclude']) {
      continue;
    }
    if (!($name = $handler
      ->label())) {
      $name = $handler
        ->ui_name();
    }
    $handler->content_field['field_name'] ? $field_name = $handler->content_field['field_name'] : ($field_name = $field);
    
    if ($view->style_plugin->options['settings']['column_width'][$field]) {
      
      $column_width = $view->style_plugin->options['settings']['column_width'][$field];
    }
    elseif ($view->style_plugin->options['columns'][$field]['width']) {
      
      $column_width = $view->style_plugin->options['columns'][$field]['width'];
    }
    else {
      $column_width = 100;
      
    }
    $column = array(
      'id' => $field,
      
      'name' => $name,
      
      'field' => $field_name,
      
      'width' => $column_width,
      'cssClass' => 'cell-title',
      'resizable' => $view->style_plugin->options['enableColumnResize'] ? 1 : 0,
    );
    
    if (is_array($handler->options['vids']) && ($vid = slickgrid_taxonomy_field_get_vid($handler))) {
      $column['vid'] = $vid;
    }
    
    if ($view->style_plugin->options['columns'][$field]['filter']) {
      $view->style_plugin->options['has_filters'] = TRUE;
      
      if ($view->style_plugin->options['collapsible_taxonomy_field'] != $field) {
        $view->style_plugin->options['showHeaderRow'] = TRUE;
      }
    }
    
    foreach (array_keys(slickgrid_get_plugin_types()) as $plugin_type) {
      
      if ($view->style_plugin->options['columns'][$field][$plugin_type]) {
        $column[$plugin_type] = $view->style_plugin->options['columns'][$field][$plugin_type];
        
        if ($plugin_type == 'editor') {
          $view->style_plugin->options['editable'] = TRUE;
          $view->style_plugin->options['enableCellNavigation'] = TRUE;
        }
      }
    }
    
    if ($view->style_plugin->options['columns'][$field]['sortable'] & !$view->style_plugin->options['collapsible_taxonomy_field']) {
      $view->style_plugin->options['sortable_columns'] = TRUE;
      $column['sortable'] = 1;
    }
    
    if ($view->style_plugin->options['collapsible_taxonomy_field'] == $field) {
      
      $view->style_plugin->options['columns'][$field]['filter'] = $column['filter'] = 'collapsibleFilter';
      $view->style_plugin->options['columns'][$field]['formatter'] = $column['formatter'] = 'collapsibleFormatter';
      $view->style_plugin->options['has_filters'] = true;
    }
    
    $view->style_plugin->options['columns'][$field]['label'] = $name;
    if (is_array($view->style_plugin->options['settings']['ordered_columns']) && is_numeric($ordered_column_position = array_search($field, $view->style_plugin->options['settings']['ordered_columns']))) {
      
      $ordered_columns[$ordered_column_position] = $column;
    }
    else {
      
      $unordered_columns[] = $column;
    }
  }
  ksort($ordered_columns);
  
  $columns = array_merge($ordered_columns, $unordered_columns);
  
  drupal_alter('slickgrid', $data, 'columns', $view);
  
  $keys = array_keys($view->field);
  $parents = array();
  
  $data = array();
  if (is_array($result)) {
    
    $ids = array();
    foreach ($result as $count => $row) {
      
      if (in_array($row->nid, $ids)) {
        drupal_set_message(t('There are duplicate nodes in this grid - each row must be unique.'), 'error');
        watchdog('slickgrid', t('Duplicate nodes - @id is not unique.'), array(
          '@id' => $row->{$view->base_field},
        ), WATCHDOG_ERROR);
        break;
      }
      else {
        
        foreach ($columns as $column) {
          if (!$view->field[$column['field']]->options['exclude']) {
            $data[$count][$column['field']] = $view->field[$column['id']]
              ->theme($row);
          }
        }
        $data[$count]['id'] = $row->nid;
        
        if (property_exists($row, $view->field['slickgrid_tid']->field_alias) && ($slickgrid_tid = $row->{$view->field['slickgrid_tid']->field_alias})) {
          if ($parent = $parents[$row->{$view->field['slickgrid_parent_tid']->field_alias}]) {
            
            $data[$count]['indent'] = $parent['indent'] + 1;
            $data[$count]['parent'] = $parent['nid'];
          }
          else {
            
            $data[$count]['indent'] = 0;
            $data[$count]['parent'] = 0;
          }
          
          $parents[$slickgrid_tid] = array(
            'nid' => $row->nid,
            'indent' => $data[$count]['indent'],
          );
        }
      }
    }
  }
  
  drupal_alter('slickgrid', $data, 'data', $view);
  $vars['slickgrid'] = theme('slickgrid', $view->style_plugin->options, $columns, $data, $view);
}
function theme_slickgrid($options, $columns, $data, $view) {
  $output = '';
  
  _slickgrid_add_files($options, $columns);
  $js = array();
  $js[] = 'var options = ' . drupal_to_js($options) . ';';
  $js[] = 'var data = [];';
  if (count($data)) {
    $js[] = 'data = ' . drupal_to_js($data) . ';';
  }
  $js[] = 'var columns = ' . _slickgrid_drupal_to_js($columns) . ';';
  $js[] = 'var slickgrid;';
  $js[] = '$(function() {';
  $js[] = 'slickgrid = new Slickgrid("#slickgrid", "' . $view->name . '", "' . $view->current_display . '", "' . url(SLICKGRID_CALLBACK_PATH) . '");';
  $js[] = '})';
  drupal_add_js(implode("\n", $js), 'inline');
  
  $output .= '<div id="slickgrid" style="width:100%;height:' . $options['viewport_height'] . 'px;" class="hideCols hideRows ' . ($options['editable'] ? 'editable' : '') . '"></div>';
  $output .= '<div id="controls">';
  if ($options['pager']) {
    $output .= '<div id="slickgrid-pager"></div>';
  }
  if ($options['undo']) {
    $output .= '<div id="slickgrid-undo" title="Undo"></div>';
  }
  $output .= '<div id="slickgrid-status"></div>';
  $output .= '<div id="slickgrid-loading-bar"></div>';
  $output .= '</div>';
  if ($options['delete_nodes']) {
    $output .= '<div id="slickgrid-delete" style="display:none;position:absolute"><h6>Delete node</h6><p>Are you sure you want to delete this node? This action cannot be undone!</p><p class="buttons"><input type="button" value="Delete" id="slickgrid-delete-button" /><input type="button" value="Cancel" id="slickgrid-cancel-delete" /></p></div>';
  }
  return $output;
}
function theme_slickgrid_views_plugin_table($form) {
  $output = drupal_render($form['description_markup']);
  $header = array(
    t('Field'),
    t('Width'),
    t('Sortable'),
  );
  $rows = array();
  $plugin_types = slickgrid_get_plugin_types();
  $header += $plugin_types;
  foreach (element_children($form['columns']) as $id) {
    $row = array();
    $row[] = drupal_render($form['columns'][$id]['name']);
    $row[] = drupal_render($form['columns'][$id]['width']);
    $row[] = drupal_render($form['columns'][$id]['sortable']);
    foreach (array_keys($plugin_types) as $plugin_type) {
      $row[] = drupal_render($form['columns'][$id][$plugin_type]);
    }
    $rows[] = $row;
  }
  $output .= theme('table', $header, $rows);
  $output .= drupal_render($form);
  return $output;
}
function slickgrid_set_settings($uid, $view_name, $settings) {
  $record = new stdClass();
  $record->uid = $uid;
  $record->view_name = $view_name;
  if ($record->settings = slickgrid_get_settings($uid, $view_name)) {
    $update = array(
      'uid',
      'view_name',
    );
  }
  else {
    $update = array();
  }
  foreach ($settings as $setting => $value) {
    $record->settings[$setting] = $value;
  }
  $record->settings = serialize($record->settings);
  drupal_write_record('slickgrid', $record, $update);
}
function slickgrid_get_settings($uid, $view_name, $setting = null) {
  $settings = unserialize(db_result(db_query("SELECT settings FROM {slickgrid} WHERE uid = %d AND view_name = '%s'", $uid, $view_name)));
  if ($setting) {
    return $settings[$setting];
  }
  else {
    return $settings;
  }
}
function slickgrid_delete_settings($params) {
  $conj = '';
  if (count($params)) {
    $sql = 'DELETE FROM {slickgrid} WHERE ';
    foreach ($params as $field => $param) {
      $sql .= $conj . $field . ' = "%s"';
      $conj = ' AND ';
    }
    db_query($sql, $params);
  }
}
function _slickgrid_add_files($options, $columns) {
  $path = drupal_get_path('module', 'slickgrid');
  drupal_add_js($path . '/js/slickgrid/lib/jquery-1.4.3.min.js');
  
  drupal_add_js($path . '/js/slickgrid/lib/firebugx.js');
  drupal_add_js($path . '/js/slickgrid/lib/jquery.event.drag-2.0.min.js');
  
  drupal_add_js($path . '/js/slickgrid/slick.core.js');
  drupal_add_js($path . '/js/slickgrid/slick.dataview.js');
  drupal_add_js($path . '/js/slickgrid/slick.grid.js');
  drupal_add_css($path . '/js/slickgrid/slick.grid.css');
  
  drupal_add_js($path . '/js/slickgrid/lib/jquery-ui-1.8.5.custom.min.js');
  drupal_add_css($path . '/js/slickgrid/css/smoothness/jquery-ui-1.8.5.custom.css');
  
  drupal_add_js($path . '/js/slickgrid/plugins/slick.checkboxselectcolumn.js');
  drupal_add_js($path . '/js/slickgrid/plugins/slick.rowselectionmodel.js');
  
  if ($options['pager']) {
    drupal_add_js($path . '/js/slickgrid/controls/slick.pager.js');
    drupal_add_css($path . '/js/slickgrid/controls/slick.pager.css');
  }
  
  if ($options['select_columns']) {
    drupal_add_js($path . '/js/slickgrid/controls/slick.columnpicker.js');
    drupal_add_css($path . '/js/slickgrid/controls/slick.columnpicker.css');
  }
  
  if ($options['grouping_field']) {
    drupal_add_js($path . '/js/slickgrid.groups-ui.js');
  }
  if ($options['collapsible_taxonomy_field']) {
    drupal_add_js($path . '/js/slickgrid.collapsible.js');
  }
  $slickgrid_plugins = slickgrid_get_plugins();
  
  foreach ($columns as $column) {
    foreach (array_keys(slickgrid_get_plugin_types()) as $plugin_type) {
      
      if (isset($column[$plugin_type])) {
        if (isset($slickgrid_plugins[$column[$plugin_type]]['js'])) {
          drupal_add_js($slickgrid_plugins[$column[$plugin_type]]['js']);
        }
        if (isset($slickgrid_plugins[$column[$plugin_type]]['css'])) {
          drupal_add_css($slickgrid_plugins[$column[$plugin_type]]['css']);
        }
      }
    }
  }
  
  drupal_add_js($path . '/js/slickgrid.js');
  drupal_add_css($path . '/css/slickgrid.css');
  
  beautytips_add_beautytips();
  
  if (module_exists('wysiwyg')) {
    $filter_formats = filter_formats();
    foreach ($filter_formats as $filter_format) {
      wysiwyg_get_profile($filter_format->format);
    }
  }
}
function _slickgrid_drupal_to_js($elements, $additional_function_names = array()) {
  $function_names = array(
    'formatter',
    'validator',
    'editor',
    'setValueHandler',
    'resizable',
  );
  if (count($additional_function_names)) {
    $function_names += $additional_function_names;
  }
  if (count($elements)) {
    $js_string = '[';
    foreach ($elements as $element) {
      $js_string .= $outer_conjunction . '{';
      foreach ($element as $element_name => $element_value) {
        $js_string .= $inner_conjunction;
        if (in_array($element_name, $function_names) || is_numeric($element_value) || is_bool($element_value)) {
          $js_string .= $element_name . ': ' . $element_value;
        }
        else {
          $js_string .= $element_name . ': ' . drupal_to_js($element_value);
        }
        $inner_conjunction = ', ';
      }
      
      $js_string .= '}';
      $inner_conjunction = ' ';
      $outer_conjunction = ', ';
    }
    $js_string .= ']';
    return $js_string;
  }
}
function slickgrid_plugins_list($reset = FALSE) {
  static $plugins;
  if (!isset($plugins) || $reset) {
    $editors = module_invoke_all('slickgrid_plugins');
    drupal_alter('slickgrid_plugins', $editors);
  }
  return (array) $editors;
}
function slickgrid_get_plugins($filters = array()) {
  
  $plugins = slickgrid_plugins_list();
  
  if (is_array($filters)) {
    $plugins = slickgrid_filter_plugins($plugins, $filters);
  }
  return $plugins;
}
function slickgrid_filter_plugins($plugins, $filters) {
  
  foreach ($plugins as $plugin_type => $plugin) {
    
    foreach ($filters as $property => $value) {
      
      if (is_array($plugin[$property]) && !in_array($value, $plugin[$property]) || is_string($plugin[$property]) && $plugin[$property] != $value) {
        
        unset($plugins[$plugin_type]);
        
        break;
      }
    }
  }
  return $plugins;
}
function slickgrid_get_plugin_types() {
  return array(
    'formatter' => t('Formatter'),
    'editor' => t('Editor'),
    'validator' => t('Validator'),
    'filter' => t('Filter'),
  );
}
function slickgrid_json($var = NULL) {
  drupal_set_header('Content-Type: text/javascript; charset=utf-8');
  if (isset($var)) {
    echo json_encode($var);
  }
  exit;
}
function slickgrid_taxonomy_field_get_vid($handler) {
  $vids = array_filter($handler->options['vids']);
  
  if (count($vids) == 1) {
    return current($vids);
  }
  return FALSE;
}
function slickgrid_views_ui_rearrange_form_submit($form, $form_state) {
  global $user;
  
  $settings = slickgrid_get_settings($user->uid, $form_state['view']->name);
  
  if (is_array($settings)) {
    slickgrid_set_settings($user->uid, $form_state['view']->name, array(
      'hidden_columns' => null,
      'ordered_columns' => null,
    ));
  }
}
function slickgrid_node_form_pre_render(&$form, &$form_state) {
  if ($_POST['vid']) {
    $vid = $_POST['vid'];
    
    $vocabularies = taxonomy_get_vocabularies($form['#node']->type);
    if ($vocabularies[$vid]->tags) {
      $form['#field_name'] = 'taxonomy[tags][' . $vid . ']';
    }
    else {
      $form['#field_name'] = 'taxonomy[' . $vid . ']';
    }
  }
  elseif ($_POST['field_name'] == 'group_nid') {
    $form['#field_name'] = 'og_groups';
  }
  elseif ($_POST['field_name'] == 'body') {
    $form['#field_name'] = 'body_field';
  }
  else {
    $form['#field_name'] = $_POST['field_name'];
  }
  
  drupal_alter('slickgrid_field', $form, $form['#field_name']);
  if (!slickgrid_callback_get_form_element($form, $form['#field_name'])) {
    drupal_set_message(t('Field not found'), 'error');
  }
  return $form;
}
function slickgrid_callback_get_form_element(&$form, $field_name) {
  static $element_exists;
  foreach (element_children($form) as $key) {
    
    if ($key === $field_name || $form[$key]['#name'] == $field_name) {
      
      if ($form['#type'] == 'fieldset') {
        unset($form['#type']);
      }
      $element_exists = true;
    }
    else {
      if (count(element_children($form[$key]))) {
        
        slickgrid_callback_get_form_element($form[$key], $field_name);
      }
      if (!count(element_children($form[$key]))) {
        
        if (!in_array($key, array(
          'form_build_id',
          'form_id',
        )) || is_numeric($key)) {
          unset($form[$key]);
        }
      }
    }
  }
  return $element_exists;
}